commit d7531876d4cf8f97528f4a81013b1ef0cbaddfae Author: Gwenhael Le Moine Date: Mon Mar 21 11:05:59 2022 +0100 import original from hpcalc.org diff --git a/BUGS.txt b/BUGS.txt new file mode 100644 index 0000000..6bfc395 --- /dev/null +++ b/BUGS.txt @@ -0,0 +1,85 @@ +2000-12-21 Ivan Cibrario Bertolotti + + * >4.1, BUG: add installation support for IRIX and Ultrix + + * >4.1, BUG: 'pack' exits with 'Chf initialization failed' + if setlocale() fails during Chf initialization; apply the same + patch as 'saturn'. + Reported by Julian Einwag + + * >4.1, BUG: Bad interaction with 'Emulate3Buttons' option + of XFree86; need key memory or key dispatch delay. + Reported by Dean Darlison + +2000-11-30 Ivan Cibrario Bertolotti + + * >3.17: with HP49 ROM 1.19-4, command-line activity + results in a lot of late hits during unconfig. Investigate + why, in this case, late hits do not result in immediate hits + in subsequent unconfigs. + + * >3.17, Saturn.ad, BUG: some X Terminals, e.g. VXT2000, + don't have an Escape key; the existing conventions, + e.g. F11 for Escape on VXT2000, conflicts with the + existing shortcuts. + +2000-11-28 Ivan Cibrario Bertolotti + + * >3.17, Saturn.ad, BUG: some X Terminals, e.g. VXT2000, only have a + Delete (*not* BackSpace) key; make sure that backspace shortcut + is available anyway in baseTranslations. + +2000-11-21 Ivan Cibrario Bertolotti + + * >3.15, BUG: xmodem file transfers don't work with + big binary objects (like lib 258); flow control? + (Fixed in 3.17) + +2000-11-20 Ivan Cibrario Bertolotti + + * >3.15: Revise init/reset sequence to be ready to accept + interrupt requests during module initialization. + + * >3.15: Reset bank switcher when '-reset' is asserted + +2000-11-16 Ivan Cibrario Bertolotti + + * >3.15, serial.c: Implement hangup on exit on pty; + kermit insists that 'A serial connection might still be active' + even when the emulator is no longer running. + (Possibly not a bug) + + * >3.15: IRIX support + (Implemented in 3.16) + + * >3.15, sutil: update with =recover code for HP49 + (Updated in 4.1) + + * >3.15, sutil: implement for HP48 + (Implemented in 4.1) + + * >=3.15, config.h, Ultrix: usleep() not supported; + disable REAL_CPU_SPEED on this platform + (Implemented in 3.16) + (NEW_FILE: config_x.h) + + * >=3.15, Imakefile, Ultrix: EXTRA_TAIL_LIBRARIES = -li + (Implemented in 3.16) + + * >=3.15, Chf, Ultrix: add default compile mode for unsupported + platforms + (Implemented in 3.16) + + * =3.15, using.texi: pty name message no longer signaled + (Fixed in 3.16) + + * >=3.15, serial.c: remove #error and install a dummy handler instead + (Fixed in 3.16; tested on already supported platforms and Ultrix) + + * >=3.15, Saturn.ad, Ultrix: Keypad's numeric keys don't work; + they work removing the leading ':' specifier + (Fixed in 3.16, both code and documentation; tested on all platforms) + + * =3.15, x11.c: remove XmNapplyCallback from file_sel_box + (Fixed in 3.16; tested on supported platforms) + \ No newline at end of file diff --git a/Chf/Bugs b/Chf/Bugs new file mode 100644 index 0000000..3ba8a2c --- /dev/null +++ b/Chf/Bugs @@ -0,0 +1,10 @@ +2001-01-25 Ivan Cibrario Bertolotti + + * Win32 support is limited to the Windows CE platform + + * _REENTRANT is not supported on Win32. + +2000-05-29 Ivan Cibrario Bertolotti + + * chf.texi: Bad punctuation characters after xref. + (Fixed in 2.2) diff --git a/Chf/Chf.h b/Chf/Chf.h new file mode 100644 index 0000000..75929fc --- /dev/null +++ b/Chf/Chf.h @@ -0,0 +1,371 @@ +/* .+ + +.identifier : $Id: Chf.h,v 2.2 2001/01/25 11:56:44 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: Chf.h,v $, main header +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 2-May-1996 +.keywords : * +.description : + This is the main header of the Condition Handling Facility + +.include : stdio.h setjmp.h (Win32: tchar.h) + +.notes : + $Log: Chf.h,v $ + Revision 2.2 2001/01/25 11:56:44 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 14:10:08 cibrario + - Revised unwind context passing mechanism; redefined CHF_NULL_CONTEXT + - New macros: CHF_NULL_HANDLER, CHF_MAJOR_RELEASE_NUMBER, + CHF_MINOR_RELEASE_NUMBER + - New ChfAction value: CHF_UNWIND_KEEP; fixed spelling of ChfAction value: + CHF_SIGNALLING -> CHF_SIGNALING + - Added structured condition handling macros: ChfTry, ChfCatch, ChfEndTry + + Revision 1.6 1997/01/15 13:41:20 cibrario + Defined the new data type ChfPointer, a generic (void *) pointer. Each + condition handler can have a private handler context pointer, of type + ChfPointer, that the function ChfPushHandler() stores and that is passed + to the handler when it's activated. + Fixed a wrong adjustment of the condition handlers stack pointer after + an unwind operation. + + Revision 1.5 1996/10/04 09:45:30 cibrario + Updated the condition message format in the private header ChfPriv.h to + improve internationalization + + Revision 1.4 1996/09/25 13:21:11 cibrario + Added macro CHF_LIBRARY_ID; it contains the current ID of the CHF library. + The module chf_init.o will contain it as a static char[] variable. + + Revision 1.2 1996/06/11 13:02:10 cibrario + Added prototype for ChfGetTopCondition() + + Revision 1.1 1996/05/28 12:56:47 cibrario + Initial revision + + +.- */ + + +/* ------------------------------------------------------------------------- + Win32 & UNICODE support + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +#define ChfChar TCHAR +#define ChfText(x) _T(x) +#define ChfSigjmp_buf jmp_buf +#define ChfSigsetjmp(x,y) setjmp(x) +#define ChfSiglongjmp(x,y) longjmp(x,y) +#else +#define ChfChar char +#define ChfText(x) x +#define ChfSigjmp_buf sigjmp_buf +#define ChfSigsetjmp(x,y) sigsetjmp(x,y) +#define ChfSiglongjmp(x,y) siglongjmp(x,y) +#endif + + +/* ------------------------------------------------------------------------- + CHF implementation limits and other symbolic constants + ------------------------------------------------------------------------- */ + +#define CHF_MAX_MESSAGE_LENGTH 256 +#define CHF_UNKNOWN_LINE_NUMBER (-1) +#define CHF_UNKNOWN_FILE_NAME (ChfChar *)NULL +#define CHF_NULL_DESCRIPTOR (ChfDescriptor *)NULL +#define CHF_NULL_CONTEXT (void *)NULL +#define CHF_NULL_POINTER (ChfPointer *)NULL +#define CHF_NULL_HANDLER (ChfHandler)NULL +#define CHF_LIBRARY_ID ChfText("$Id: Chf.h,v 2.2 2001/01/25 11:56:44 cibrario Exp $") + +#define CHF_MAJOR_RELEASE_NUMBER 2 +#define CHF_MINOR_RELEASE_NUMBER 2 + +#define CHF_MODULE_NAMES_SET 1 +#define CHF_SET 2 +#define CHF_ERRNO_SET 3 + + +/* ------------------------------------------------------------------------- + Condition codes + ------------------------------------------------------------------------- */ + +#define CHF_S_OK 0 +#define CHF_F_COND_STACK_FULL 1 /* Condition stack is full */ +#define CHF_F_HDLR_STACK_FULL 2 /* Handler stack is full */ +#define CHF_F_HDLR_STACK_EMPTY 3 /* Handler stack is empty */ +#define CHF_F_BAD_STATE 4 /* Bad CHF state for req. operation */ +#define CHF_F_INVALID_ACTION 5 /* Invalid action from handler: %d */ +#define CHF_F_MALLOC 6 /* Dynamic memory allocation failed */ +#define CHF_F_NOT_AVAILABLE 7 /* Function not available */ +#define CHF_F_SETLOCALE 10 /* setlocale() failed */ +#define CHF_F_CATOPEN 11 /* catopen() failed */ + + +/* ------------------------------------------------------------------------- + Type definitions + ------------------------------------------------------------------------- */ + +typedef enum /* Condition severity codes */ +{ + CHF_SUCCESS, + CHF_INFO, + CHF_WARNING, + CHF_ERROR, + CHF_FATAL +} + ChfSeverity; + +typedef enum /* Condition handler action codes */ +{ + CHF_CONTINUE, /* Continue application */ + CHF_RESIGNAL, /* Resignal to next handler */ + CHF_UNWIND, /* Stack unwind */ + CHF_UNWIND_KEEP /* Unwind, keep last cond. group */ +} + ChfAction; + +typedef int /* CHF options */ + ChfOptions; + +#define CHF_DEFAULT 0x0000 /* default flags */ +#define CHF_ABORT 0x0001 /* use abort() instead of exit() */ + +typedef enum /* Current CHF state */ +{ + CHF_UNKNOWN, + CHF_IDLE, + CHF_SIGNALING, + CHF_UNWINDING, + CHF_SIGNAL_UNWINDING +} + ChfState; + +typedef struct ChfDescriptor_S /* Condition descriptor */ +{ + int module_id; /* Module identifier */ + int condition_code; /* Condition code */ + ChfSeverity severity; /* Severity */ + int line_number; /* Line # or CHF_UNK_LINE_NUMBER */ + const ChfChar *file_name; /* File name or CHF_UNK_FILE_NAME */ + ChfChar message[CHF_MAX_MESSAGE_LENGTH]; /* Partial message */ + struct ChfDescriptor_S *next; /* Link to next descriptor */ +} + ChfDescriptor; + +typedef struct ChfTable_S /* Standalone message table */ +{ + int module; /* Module identifier */ + int code; /* Condition code */ + ChfChar *msg_template; /* Message template */ +} + ChfTable; + +typedef /* Generic pointer */ + void *ChfPointer; + +typedef /* Condition handler */ + ChfAction (*ChfHandler)( + const ChfDescriptor *, + const ChfState, + ChfPointer +); + +typedef /* Message retrieval 'get_message' function */ + const ChfChar * (*ChfMrsGet)( + void *, + const int, + const int, + const ChfChar *default_message +); + +typedef /* Message retrieval 'exit' function */ + void (*ChfMrsExit)( + void * +); + + +/* ------------------------------------------------------------------------- + Condition generation macros + ------------------------------------------------------------------------- */ + +#if defined(CHF_EXTENDED_INFO) +#define ChfCondition \ + ChfGenerate( \ + CHF_MODULE_ID, \ + ChfText(__FILE__), __LINE__, + +#ifdef _WIN32 +#define ChfErrnoCondition +#else +#define ChfErrnoCondition \ + ChfGenerate( \ + CHF_ERRNO_SET, \ + ChfText(__FILE__), __LINE__, \ + errno, \ + CHF_ERROR \ + ) +#endif + +#else +#define ChfCondition \ + ChfGenerate( \ + CHF_MODULE_ID, \ + CHF_UNKNOWN_FILE_NAME, CHF_UNKNOWN_LINE_NUMBER, + +#ifdef _WIN32 +#define ChfErrnoCondition +#else +#define ChfErrnoCondition \ + ChfGenerate( \ + CHF_ERRNO_SET, \ + CHF_UNKNOWN_FILE_NAME, CHF_UNKNOWN_LINE_NUMBER, \ + errno, \ + CHF_ERROR \ + ) +#endif + +#endif + +#define ChfEnd \ + ) + + +/* ------------------------------------------------------------------------- + Structured condition handling + ------------------------------------------------------------------------- */ + +#define ChfTry \ + {\ + ChfSigjmp_buf _chf_sigjmp_buf;\ + if(ChfSigsetjmp(_chf_sigjmp_buf, 1) == 0)\ + {\ + ChfPushHandler(CHF_NULL_HANDLER, _chf_sigjmp_buf, CHF_NULL_POINTER); + +#define ChfCatch \ + ChfPopHandler();\ + }\ + else\ + { + +#define ChfEndTry \ + ChfDiscard();\ + }\ + } + + +/* ------------------------------------------------------------------------- + Other macros + ------------------------------------------------------------------------- */ + +#define ChfGetNextDescriptor(d) (d)->next +#define ChfGetModuleId(d) (d)->module_id +#define ChfGetConditionCode(d) (d)->condition_code +#define ChfGetSeverity(d) (d)->severity +#define ChfGetLineNumber(d) (d)->line_number +#define ChfGetFileName(d) (d)->file_name +#define ChfGetPartialMessage(d) (d)->message + + + +/* ------------------------------------------------------------------------- + Function prototypes + ------------------------------------------------------------------------- */ + +int ChfInit( /* Generic initialization */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + void *mrs_data, /* Message retrieval private data */ + ChfMrsGet mrs_get, /* 'GetMessage' function */ + ChfMrsExit mrs_exit, /* 'Exit' function */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +int ChfMsgcatInit( /* Initialization with msgcat subsystem */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfChar *msgcat_name, /* Name of the message catalog */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +int ChfStaticInit( /* Initialization with static message tables */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfTable *table, /* Static message table */ + const size_t table_size, /* Size of the message table */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +int ChfWin32Init( /* Initialization within _WIN32 */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ +#ifndef _WIN32 + void *instance, /* Fake arguments */ +#else + HINSTANCE instance, /* App. instance handle */ +#endif + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +void ChfExit( /* Exit */ + void +); + +void ChfAbort( /* Abort application */ + const int abort_code +); + +void ChfPushHandler( /* Push a new handler into the stack */ + ChfHandler new_handler, /* Handler to be added */ + void *unwind_context, /* Unwind context */ + ChfPointer handler_context /* Private handler context */ +); + +void ChfPopHandler( /* Pop a handler */ + void +); + +ChfChar *ChfBuildMessage( /* Build a condition message */ + const ChfDescriptor *descriptor +); + +void ChfSignal( /* Signal the current conditions */ + void +); + +void ChfDiscard( /* Discard the current conditions */ + void +); + +void ChfGenerate( /* Generate a condition into the stack */ + const int module_id, + const ChfChar *file_name, + const int line_number, + const int condition_code, + const ChfSeverity severity, + ... +); + +const ChfChar *ChfGetMessage( /* Retrieve a condition message */ + const int module_id, + const int condition_code, + const ChfChar *default_message +); + +const ChfDescriptor *ChfGetTopCondition( /* Retrieve top condition */ + void +); diff --git a/Chf/ChfPriv.h b/Chf/ChfPriv.h new file mode 100644 index 0000000..11df1ce --- /dev/null +++ b/Chf/ChfPriv.h @@ -0,0 +1,155 @@ +/* .+ + +.identifier : $Id: ChfPriv.h,v 2.2 2001/01/25 11:57:57 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: ChfPriv.h,v $, main header +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 2-May-1996 +.keywords : * +.description : + This is the private header of the Condition Handling Facility + +.include : * + +.notes : + $Log: ChfPriv.h,v $ + Revision 2.2 2001/01/25 11:57:57 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 14:14:36 cibrario + - Defined new abort codes: CHF_ABORT_GET_CONTEXT, CHF_ABORT_PTHREAD + - Redefined .unwind_context field of ChfHandlerDescriptor + - Conditional retarget of chf_context for multithreading support + - Declared new private function: _ChfGetContext() (mt support only) + + Revision 1.6 1997/01/15 13:38:24 cibrario + Added the new field .handler_context to struct ChfHandlerDescriptor_S, to + store, for each condition handler, the private handler context pointer. + + Revision 1.5 1996/10/04 09:43:51 cibrario + Updated the condition message format to improve internationalization + + Revision 1.1 1996/05/28 12:56:37 cibrario + Initial revision + + +.- */ + + +/* ------------------------------------------------------------------------- + Macros + ------------------------------------------------------------------------- */ + +#define CHF_MODULE_ID CHF_SET +#define CHF_TMP_MESSAGE_LENGTH (2*CHF_MAX_MESSAGE_LENGTH) +#define CHF_DEF_MESSAGE_LENGTH 40 +#define CHF_DEF_PARTIAL_MSG_FMT ChfText("Code <%d>d") +#define CHF_DEF_MID_MSG_FMT ChfText("Mid <%d>d") +#define CHF_EXTENDED_INFO_FMT ChfText("(%s,%d)") +#define CHF_SEVERITY_NAMES \ +{ ChfText("S"), ChfText("I"), ChfText("W"), ChfText("E"), ChfText("F") } +#define CHF_UNKNOWN_SEVERITY ChfText("?") +#define CHF_MESSAGE_SEPARATOR ChfText("-") +#define CHF_MESSAGE_TERMINATOR ChfText("\n") +#define CHF_ABORT_HEADER ChfText("ChfAbort-F-") +#define CHF_ABORT_BAD_CODE_FMT ChfText("Bad abort code <%d>d\n") +#define CHF_ABORT_GOOD_CODE_FMT ChfText("%s\n") + + +/* ------------------------------------------------------------------------- + Abort codes used with ChfAbort() + ------------------------------------------------------------------------- */ + +#define CHF_ABORT_SILENT 0 +#define CHF_ABORT_INIT 1 +#define CHF_ABORT_MSG_OVF 2 +#define CHF_ABORT_INVALID_ACTION 3 +#define CHF_ABORT_DUP_INIT 4 +#define CHF_ABORT_ALREADY_UNWINDING 5 +#define CHF_ABORT_IMPROPERLY_HANDLED 6 +#define CHF_ABORT_FATAL_UNWINDING 7 +#define CHF_ABORT_COND_STACK_OVF 8 +#define CHF_ABORT_GET_CONTEXT 9 +#define CHF_ABORT_PTHREAD 10 + + + +/* ------------------------------------------------------------------------- + Type definitions + ------------------------------------------------------------------------- */ + +typedef struct ChfHandlerDescriptor_S +{ + ChfHandler handler; + void *unwind_context; + ChfPointer handler_context; +} + ChfHandlerDescriptor; + +typedef struct ChfContext_S /* CHF Context */ +{ + ChfState state; /* Current CHF state */ + const ChfChar *app_name; /* Application's name */ + ChfOptions options; /* Options */ + void *mrs_data; /* Message retrieval private data */ + ChfMrsGet mrs_get; /* 'GetMessage' function */ + ChfMrsExit mrs_exit; /* 'Exit' function */ + int condition_stack_size; /* Size of the condition stack */ + int handler_stack_size; /* Size of the handler stack */ + int exit_code; /* Abnormal exit code */ + ChfDescriptor *condition_stack; /* Condition stack */ + ChfDescriptor *condition_base; /* Current condition stack base */ + ChfDescriptor *condition_sp; /* Current condition stack pointer */ + ChfHandlerDescriptor *handler_stack; /* Handler stack */ + ChfHandlerDescriptor *handler_sp; /* Current handler stack pointer */ + ChfChar *message_buffer; /* Message buffer */ +} + ChfContext; + + +/* ------------------------------------------------------------------------- + Multithreading support + ------------------------------------------------------------------------- */ +#ifdef _REENTRANT +#define chf_context (*_ChfGetContext()) +#else +#define chf_context _chf_context +#endif + + +/* ------------------------------------------------------------------------- + Global variables + ------------------------------------------------------------------------- */ + +extern ChfContext _chf_context; /* CHF Context */ + + +/* ------------------------------------------------------------------------- + Private function prototypes + ------------------------------------------------------------------------- */ +#ifdef _REENTRANT +ChfContext *_ChfGetContext(void); +#endif + + +/* ------------------------------------------------------------------------- + Private redirection of stdlib functions needed by Win32 + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +#define ChfStrlen _tcslen +#define ChfStrcpy _tcscpy +#define ChfStrcat _tcscat +#define ChfStrncpy _tcsncpy +#define ChfSprintf _stprintf +#define ChfVsprintf _vstprintf +#else +#define ChfStrlen strlen +#define ChfStrcpy strcpy +#define ChfStrcat strcat +#define ChfStrncpy strncpy +#define ChfSprintf sprintf +#define ChfVsprintf vsprintf +#endif diff --git a/Chf/Makefile b/Chf/Makefile new file mode 100644 index 0000000..97c98cd --- /dev/null +++ b/Chf/Makefile @@ -0,0 +1,209 @@ +# ICB, 2-Oct-2000 +# Minor off-line changes to support stand-alone build in saturn +# +# ICB, 16-Nov-2000 +# Off-line change: added default compile mode for unsupported platforms + +# .+ +# +# .identifier : $Id: Makefile,v 2.1 2000/05/29 13:53:07 cibrario Rel $ +# .context : CHF, Condition Handling Facility +# .title : $RCSfile: Makefile,v $, Makefile +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : 3-May-1996 +# .keywords : * +# .description : +# Makefile for the CHF library +# .notes : +# $Log: Makefile,v $ +# Revision 2.1 2000/05/29 13:53:07 cibrario +# - Deep revision to support transparent, multi-platform builds +# - Added multithreading support +# - New/revised targets: test, doc, clean +# +# Revision 1.2 1996/06/11 13:03:06 cibrario +# Added new module chf_top.c +# +# Revision 1.1 1996/05/29 09:12:10 cibrario +# Initial revision +# +# +# .- + +# +# Platform-dependent definitions. Edit when required, for example to +# add a new platform. +# +# There is a set of definitions per platform; the prefix or the definition +# names is the value returned by a plain 'uname' on that platform. +# +# CC: cc compiler +# TS_CFLAGS: cc flags to enable multithreading +# TS_LOADLIBES: ld flags and additional libraries to enable multithreading +# F_CFLAGS: cc feature test macros +# O_CFLAGS: cc optimization control flags and other, additional flags +# +# Supported platforms: +# OSF1 Digital UNIX v4.0 +# SunOS Solaris v2.6 +# Linux Linux 2.1 +# +# In addition, if the first build fails, a second attempt is made +# using the X_CC and X_CFLAGS fallback values. + +OSF1_CC = cc +OSF1_TS_CFLAGS = -pthread +OSF1_TS_LOADLIBES = -pthread +OSF1_F_CFLAGS = "-D_POSIX_C_SOURCE=199506L -D_XOPEN_SOURCE" +OSF1_O_CFLAGS = "-O3 -std1" + +SunOS_CC = gcc +SunOS_TS_CFLAGS = -D_REENTRANT +SunOS_TS_LOADLIBES = -lpthread +SunOS_F_CFLAGS = -D_POSIX_C_SOURCE=199506L +SunOS_O_CFLAGS = "-O3 -ansi -pedantic" + +Linux_CC = cc +Linux_TS_CFLAGS = -D_REENTRANT +Linux_TS_LOADLIBES = -lpthread +Linux_F_CFLAGS = -D_POSIX_C_SOURCE=199506L +Linux_O_CFLAGS = "-O3 -ansi -pedantic" + +# +# Destination paths for library objects, C headers, binaries, message catalog +# sources and X resource files. Edit as required. +# +DESTLIBDIR = $(HOME)/lib +DESTHDRDIR = $(HOME)/include +DESTBINDIR = $(HOME)/bin +DESTMSFDIR = $(HOME)/msf +DESTRESDIR = $(HOME)/res + +# +# The default target recursively invokes make defining UNAME to the +# string returned by uname; this is used to make appropriate +# platform-dependent definitions above. +# +all: + @$(MAKE) UNAME=`uname` build || \ + $(MAKE) UNAME=Unknown CC='"$(X_CC)"' O_CFLAGS='"$(X_CFLAGS)"' build + +# +# Vectored definitions; see above and do not edit. +# +CC = $($(UNAME)_CC) +TS_CFLAGS = $($(UNAME)_TS_CFLAGS) +TS_LOADLIBES = $($(UNAME)_TS_LOADLIBES) +F_CFLAGS = $($(UNAME)_F_CFLAGS) +O_CFLAGS = $($(UNAME)_O_CFLAGS) + +# +# Include common definitions +# +include Makefile_def + +# +# Build directories for single-threaded and multi-threaded lib versions +# +ST_BUILD_DIR = ./st_build +MT_BUILD_DIR = ./mt_build + +# +# Library names +# +ST_LIB_NAME = libChf.a +MT_LIB_NAME = libChf_r.a + +# +# Library paths +# +ST_LIB_PATH = $(ST_BUILD_DIR)/$(ST_LIB_NAME) +MT_LIB_PATH = $(MT_BUILD_DIR)/$(MT_LIB_NAME) + +# +# build: Create and populate build directories; for each build directory, +# make dependencies and rebuild +# +build: $(ST_LIB_PATH) $(MT_LIB_PATH) + +# +# test: Build and executes test programs +# +test: + @$(MAKE) UNAME=`uname` do_test + @echo "All tests completed successfully" + +do_test: $(ST_LIB_PATH) $(MT_LIB_PATH) + @cd $(ST_BUILD_DIR) && \ + $(MAKE) test CC=$(CC) LIB=$(ST_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) > test.log 2>&1 + @cd $(MT_BUILD_DIR) && \ + $(MAKE) test CC=$(CC) LIB=$(MT_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) \ + TS_CFLAGS=$(TS_CFLAGS) TS_LOADLIBES=$(TS_LOADLIBES) >> test.log 2>&1 + +# +# doc: Make documentation files +# +doc: + makeinfo chf.texi + texi2dvi chf.texi; dvips -o chf.ps chf.dvi + +# +# clean: Remove build directories +# +clean: + rm -rf $(ST_BUILD_DIR) $(MT_BUILD_DIR) + +# +# install: Move all files to their directories +# ICB, 23-Oct-2000: Do nothing +# +install: $(ST_LIB_PATH) $(MT_LIB_PATH) $(HDR) $(MSF) $(RES) + +# +# Prepare the build directories for use +# +# ICB, 16-Nov-2000 +# Off-line change: Force the build to fail with exit 1 if CC is undefined +# +$(ST_BUILD_DIR) $(MT_BUILD_DIR): + @[ "$(CC)" = "" ] && \ + { echo "Warning: using X_ defaults"; exit 1; } ; \ + mkdir $@ && \ + cd $@ && \ + ln -s ../Makefile_sub Makefile && \ + for s in $(SRC) $(TSRC) $(HDR) $(MSF) $(TMSF) $(RES) ; do \ + ln -s ../$$s $$s ; \ + done + +# +# Make the libraries; the rebuild is forced here because only the +# sub-makes have module dependencies. +# +# ICB, 16-Nov-2000 +# Off-line change: Force the build to fail with exit 1 if CC is undefined +# +$(ST_LIB_PATH): $(ST_BUILD_DIR) force + @[ "$(CC)" = "" ] && \ + { echo "Warning: using X_ defaults"; exit 1; } ; \ + cd $(ST_BUILD_DIR) && \ + $(MAKE) depend CC=$(CC) LIB=$(ST_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) && \ + $(MAKE) all CC=$(CC) LIB=$(ST_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) + +$(MT_LIB_PATH): $(MT_BUILD_DIR) force + @[ "$(CC)" = "" ] && \ + { echo "Warning: using X_ defaults"; exit 1; } ; \ + cd $(MT_BUILD_DIR) && \ + $(MAKE) depend CC=$(CC) LIB=$(MT_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) \ + TS_CFLAGS=$(TS_CFLAGS) TS_LOADLIBES=$(TS_LOADLIBES) && \ + $(MAKE) all CC=$(CC) LIB=$(MT_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) \ + TS_CFLAGS=$(TS_CFLAGS) TS_LOADLIBES=$(TS_LOADLIBES) + +force: diff --git a/Chf/Makefile_def b/Chf/Makefile_def new file mode 100644 index 0000000..3f6db89 --- /dev/null +++ b/Chf/Makefile_def @@ -0,0 +1,68 @@ +# .+ +# +# .identifier : $Id: Makefile_def,v 2.2 2001/01/25 11:59:19 cibrario Exp $ +# .context : CHF, Condition Handling Facility +# .title : $RCSfile: Makefile_def,v $, Makefile +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : 22-May-2000 +# .keywords : * +# .description : +# Makefile for CHF library common definitions; no platform dependencies here. +# .notes : +# $Log: Makefile_def,v $ +# Revision 2.2 2001/01/25 11:59:19 cibrario +# Added partial Win32 support (Windows CE only). +# +# Revision 2.1 2000/05/29 13:08:39 cibrario +# *** empty log message *** +# +# +# .- + +# +# Source files +# +SRC = chf_init.c chf_gen.c chf_sig.c chf_abrt.c chf_hdlr.c \ + chf_msgc.c chf_st.c \ + chf_top.c \ + chf_win32.c + +TSRC = test01.c test02.c test03.c test04.c test05.c test06.c \ + test07.c + +HDR = Chf.h ChfPriv.h + +MSF = chf.msf + +TMSF = test01.msf + +RES = + +# +# Target object files +# +OBJ = $(SRC:.c=.o) + +# +# Target test programs +# +TBIN = $(TSRC:.c=) + +# +# Other definitions +# +HPATH = -I. +LPATH = -L. + +CFLAGS = $(F_CFLAGS) $(O_CFLAGS) $(TS_CFLAGS) $(U_CFLAGS) $(HPATH) +LFLAGS = $(F_CFLAGS) $(L_CFLAGS) $(TS_LFLAGS) $(U_CFLAGS) $(HPATH) +LDFLAGS = $(LPATH) + +LOADLIBES = $(LIB) $(TS_LOADLIBES) $(I_LOADLIBES) $(U_LOADLIBES) + +# +# Solaris spelling of LOADLIBES +# +LDLIBS = $(LOADLIBES) diff --git a/Chf/Makefile_sub b/Chf/Makefile_sub new file mode 100644 index 0000000..9071939 --- /dev/null +++ b/Chf/Makefile_sub @@ -0,0 +1,40 @@ +# .+ +# +# .identifier : $Id: Makefile_sub,v 2.1 2000/05/29 13:08:51 cibrario Rel $ +# .context : CHF, Condition Handling Facility +# .title : $RCSfile: Makefile_sub,v $, Makefile +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : 22-May-2000 +# .keywords : * +# .description : +# Makefile for CHF library subdirectories +# .notes : +# $Log: Makefile_sub,v $ +# Revision 2.1 2000/05/29 13:08:51 cibrario +# *** empty log message *** +# +# +# .- + +# +# Include common definitions +# +include ../Makefile_def + +# +# Targets +# +all: $(LIB) + +$(LIB): $(OBJ) + $(AR) $(ARFLAGS) $@ $? + +test: $(TBIN) + @for b in $(TBIN) ; do \ + ./$$b ; \ + done + +depend: $(SRC) + @makedepend $(HPATH) $(SRC) diff --git a/Chf/RCS/Bugs,v b/Chf/RCS/Bugs,v new file mode 100644 index 0000000..ec64c2b --- /dev/null +++ b/Chf/RCS/Bugs,v @@ -0,0 +1,52 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @# @; + + +2.2 +date 2001.01.25.11.53.35; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.29.14.44.41; author cibrario; state Rel; +branches; +next ; + + +desc +@@ + + +2.2 +log +@*** empty log message *** +@ +text +@2001-01-25 Ivan Cibrario Bertolotti + + * Win32 support is limited to the Windows CE platform + + * _REENTRANT is not supported on Win32. + +2000-05-29 Ivan Cibrario Bertolotti + + * chf.texi: Bad punctuation characters after xref. + (Fixed in 2.2) +@ + + +2.1 +log +@*** empty log message *** +@ +text +@d1 6 +d10 1 +a10 1 + +@ diff --git a/Chf/RCS/Chf.h,v b/Chf/RCS/Chf.h,v new file mode 100644 index 0000000..13b76ea --- /dev/null +++ b/Chf/RCS/Chf.h,v @@ -0,0 +1,611 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.11.56.44; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.10.08; author cibrario; state Rel; +branches; +next 1.6; + +1.6 +date 97.01.15.13.41.20; author cibrario; state Exp; +branches; +next 1.5; + +1.5 +date 96.10.04.09.45.30; author cibrario; state Beta; +branches; +next 1.4; + +1.4 +date 96.09.25.13.21.11; author cibrario; state Exp; +branches; +next 1.2; + +1.2 +date 96.06.11.13.02.10; author cibrario; state Exp; +branches; +next 1.1; + +1.1 +date 96.05.28.12.56.47; author cibrario; state Exp; +branches; +next ; + + +desc +@This is the main header of the Condition Handling Facility +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: Chf.h,v 2.1 2000/05/26 14:10:08 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: Chf.h,v $, main header +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 2-May-1996 +.keywords : * +.description : + This is the main header of the Condition Handling Facility + +.include : stdio.h setjmp.h (Win32: tchar.h) + +.notes : + $Log: Chf.h,v $ + Revision 2.1 2000/05/26 14:10:08 cibrario + - Revised unwind context passing mechanism; redefined CHF_NULL_CONTEXT + - New macros: CHF_NULL_HANDLER, CHF_MAJOR_RELEASE_NUMBER, + CHF_MINOR_RELEASE_NUMBER + - New ChfAction value: CHF_UNWIND_KEEP; fixed spelling of ChfAction value: + CHF_SIGNALLING -> CHF_SIGNALING + - Added structured condition handling macros: ChfTry, ChfCatch, ChfEndTry + + Revision 1.6 1997/01/15 13:41:20 cibrario + Defined the new data type ChfPointer, a generic (void *) pointer. Each + condition handler can have a private handler context pointer, of type + ChfPointer, that the function ChfPushHandler() stores and that is passed + to the handler when it's activated. + Fixed a wrong adjustment of the condition handlers stack pointer after + an unwind operation. + + Revision 1.5 1996/10/04 09:45:30 cibrario + Updated the condition message format in the private header ChfPriv.h to + improve internationalization + + Revision 1.4 1996/09/25 13:21:11 cibrario + Added macro CHF_LIBRARY_ID; it contains the current ID of the CHF library. + The module chf_init.o will contain it as a static char[] variable. + + Revision 1.2 1996/06/11 13:02:10 cibrario + Added prototype for ChfGetTopCondition() + + Revision 1.1 1996/05/28 12:56:47 cibrario + Initial revision + + +.- */ + + +/* ------------------------------------------------------------------------- + Win32 & UNICODE support + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +#define ChfChar TCHAR +#define ChfText(x) _T(x) +#define ChfSigjmp_buf jmp_buf +#define ChfSigsetjmp(x,y) setjmp(x) +#define ChfSiglongjmp(x,y) longjmp(x,y) +#else +#define ChfChar char +#define ChfText(x) x +#define ChfSigjmp_buf sigjmp_buf +#define ChfSigsetjmp(x,y) sigsetjmp(x,y) +#define ChfSiglongjmp(x,y) siglongjmp(x,y) +#endif + + +/* ------------------------------------------------------------------------- + CHF implementation limits and other symbolic constants + ------------------------------------------------------------------------- */ + +#define CHF_MAX_MESSAGE_LENGTH 256 +#define CHF_UNKNOWN_LINE_NUMBER (-1) +#define CHF_UNKNOWN_FILE_NAME (ChfChar *)NULL +#define CHF_NULL_DESCRIPTOR (ChfDescriptor *)NULL +#define CHF_NULL_CONTEXT (void *)NULL +#define CHF_NULL_POINTER (ChfPointer *)NULL +#define CHF_NULL_HANDLER (ChfHandler)NULL +#define CHF_LIBRARY_ID ChfText("$Id: Chf.h,v 2.1 2000/05/26 14:10:08 cibrario Exp $") + +#define CHF_MAJOR_RELEASE_NUMBER 2 +#define CHF_MINOR_RELEASE_NUMBER 2 + +#define CHF_MODULE_NAMES_SET 1 +#define CHF_SET 2 +#define CHF_ERRNO_SET 3 + + +/* ------------------------------------------------------------------------- + Condition codes + ------------------------------------------------------------------------- */ + +#define CHF_S_OK 0 +#define CHF_F_COND_STACK_FULL 1 /* Condition stack is full */ +#define CHF_F_HDLR_STACK_FULL 2 /* Handler stack is full */ +#define CHF_F_HDLR_STACK_EMPTY 3 /* Handler stack is empty */ +#define CHF_F_BAD_STATE 4 /* Bad CHF state for req. operation */ +#define CHF_F_INVALID_ACTION 5 /* Invalid action from handler: %d */ +#define CHF_F_MALLOC 6 /* Dynamic memory allocation failed */ +#define CHF_F_NOT_AVAILABLE 7 /* Function not available */ +#define CHF_F_SETLOCALE 10 /* setlocale() failed */ +#define CHF_F_CATOPEN 11 /* catopen() failed */ + + +/* ------------------------------------------------------------------------- + Type definitions + ------------------------------------------------------------------------- */ + +typedef enum /* Condition severity codes */ +{ + CHF_SUCCESS, + CHF_INFO, + CHF_WARNING, + CHF_ERROR, + CHF_FATAL +} + ChfSeverity; + +typedef enum /* Condition handler action codes */ +{ + CHF_CONTINUE, /* Continue application */ + CHF_RESIGNAL, /* Resignal to next handler */ + CHF_UNWIND, /* Stack unwind */ + CHF_UNWIND_KEEP /* Unwind, keep last cond. group */ +} + ChfAction; + +typedef int /* CHF options */ + ChfOptions; + +#define CHF_DEFAULT 0x0000 /* default flags */ +#define CHF_ABORT 0x0001 /* use abort() instead of exit() */ + +typedef enum /* Current CHF state */ +{ + CHF_UNKNOWN, + CHF_IDLE, + CHF_SIGNALING, + CHF_UNWINDING, + CHF_SIGNAL_UNWINDING +} + ChfState; + +typedef struct ChfDescriptor_S /* Condition descriptor */ +{ + int module_id; /* Module identifier */ + int condition_code; /* Condition code */ + ChfSeverity severity; /* Severity */ + int line_number; /* Line # or CHF_UNK_LINE_NUMBER */ + const ChfChar *file_name; /* File name or CHF_UNK_FILE_NAME */ + ChfChar message[CHF_MAX_MESSAGE_LENGTH]; /* Partial message */ + struct ChfDescriptor_S *next; /* Link to next descriptor */ +} + ChfDescriptor; + +typedef struct ChfTable_S /* Standalone message table */ +{ + int module; /* Module identifier */ + int code; /* Condition code */ + ChfChar *msg_template; /* Message template */ +} + ChfTable; + +typedef /* Generic pointer */ + void *ChfPointer; + +typedef /* Condition handler */ + ChfAction (*ChfHandler)( + const ChfDescriptor *, + const ChfState, + ChfPointer +); + +typedef /* Message retrieval 'get_message' function */ + const ChfChar * (*ChfMrsGet)( + void *, + const int, + const int, + const ChfChar *default_message +); + +typedef /* Message retrieval 'exit' function */ + void (*ChfMrsExit)( + void * +); + + +/* ------------------------------------------------------------------------- + Condition generation macros + ------------------------------------------------------------------------- */ + +#if defined(CHF_EXTENDED_INFO) +#define ChfCondition \ + ChfGenerate( \ + CHF_MODULE_ID, \ + ChfText(__FILE__), __LINE__, + +#ifdef _WIN32 +#define ChfErrnoCondition +#else +#define ChfErrnoCondition \ + ChfGenerate( \ + CHF_ERRNO_SET, \ + ChfText(__FILE__), __LINE__, \ + errno, \ + CHF_ERROR \ + ) +#endif + +#else +#define ChfCondition \ + ChfGenerate( \ + CHF_MODULE_ID, \ + CHF_UNKNOWN_FILE_NAME, CHF_UNKNOWN_LINE_NUMBER, + +#ifdef _WIN32 +#define ChfErrnoCondition +#else +#define ChfErrnoCondition \ + ChfGenerate( \ + CHF_ERRNO_SET, \ + CHF_UNKNOWN_FILE_NAME, CHF_UNKNOWN_LINE_NUMBER, \ + errno, \ + CHF_ERROR \ + ) +#endif + +#endif + +#define ChfEnd \ + ) + + +/* ------------------------------------------------------------------------- + Structured condition handling + ------------------------------------------------------------------------- */ + +#define ChfTry \ + {\ + ChfSigjmp_buf _chf_sigjmp_buf;\ + if(ChfSigsetjmp(_chf_sigjmp_buf, 1) == 0)\ + {\ + ChfPushHandler(CHF_NULL_HANDLER, _chf_sigjmp_buf, CHF_NULL_POINTER); + +#define ChfCatch \ + ChfPopHandler();\ + }\ + else\ + { + +#define ChfEndTry \ + ChfDiscard();\ + }\ + } + + +/* ------------------------------------------------------------------------- + Other macros + ------------------------------------------------------------------------- */ + +#define ChfGetNextDescriptor(d) (d)->next +#define ChfGetModuleId(d) (d)->module_id +#define ChfGetConditionCode(d) (d)->condition_code +#define ChfGetSeverity(d) (d)->severity +#define ChfGetLineNumber(d) (d)->line_number +#define ChfGetFileName(d) (d)->file_name +#define ChfGetPartialMessage(d) (d)->message + + + +/* ------------------------------------------------------------------------- + Function prototypes + ------------------------------------------------------------------------- */ + +int ChfInit( /* Generic initialization */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + void *mrs_data, /* Message retrieval private data */ + ChfMrsGet mrs_get, /* 'GetMessage' function */ + ChfMrsExit mrs_exit, /* 'Exit' function */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +int ChfMsgcatInit( /* Initialization with msgcat subsystem */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfChar *msgcat_name, /* Name of the message catalog */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +int ChfStaticInit( /* Initialization with static message tables */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfTable *table, /* Static message table */ + const size_t table_size, /* Size of the message table */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +int ChfWin32Init( /* Initialization within _WIN32 */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ +#ifndef _WIN32 + void *instance, /* Fake arguments */ +#else + HINSTANCE instance, /* App. instance handle */ +#endif + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +); + +void ChfExit( /* Exit */ + void +); + +void ChfAbort( /* Abort application */ + const int abort_code +); + +void ChfPushHandler( /* Push a new handler into the stack */ + ChfHandler new_handler, /* Handler to be added */ + void *unwind_context, /* Unwind context */ + ChfPointer handler_context /* Private handler context */ +); + +void ChfPopHandler( /* Pop a handler */ + void +); + +ChfChar *ChfBuildMessage( /* Build a condition message */ + const ChfDescriptor *descriptor +); + +void ChfSignal( /* Signal the current conditions */ + void +); + +void ChfDiscard( /* Discard the current conditions */ + void +); + +void ChfGenerate( /* Generate a condition into the stack */ + const int module_id, + const ChfChar *file_name, + const int line_number, + const int condition_code, + const ChfSeverity severity, + ... +); + +const ChfChar *ChfGetMessage( /* Retrieve a condition message */ + const int module_id, + const int condition_code, + const ChfChar *default_message +); + +const ChfDescriptor *ChfGetTopCondition( /* Retrieve top condition */ + void +); +@ + + +2.1 +log +@- Revised unwind context passing mechanism; redefined CHF_NULL_CONTEXT +- New macros: CHF_NULL_HANDLER, CHF_MAJOR_RELEASE_NUMBER, + CHF_MINOR_RELEASE_NUMBER +- New ChfAction value: CHF_UNWIND_KEEP; fixed spelling of ChfAction value: + CHF_SIGNALLING -> CHF_SIGNALING +- Added structured condition handling macros: ChfTry, ChfCatch, ChfEndTry +@ +text +@d3 1 +a3 1 +.identifier : $Id: Chf.h,v 1.6 1997/01/15 13:41:20 cibrario Exp cibrario $ +d14 1 +a14 1 +.include : stdio.h setjmp.h +d18 8 +d53 19 +d77 1 +a77 1 +#define CHF_UNKNOWN_FILE_NAME (char *)NULL +d82 1 +a82 1 +#define CHF_LIBRARY_ID "$Id: Chf.h,v 1.6 1997/01/15 13:41:20 cibrario Exp cibrario $" +d85 1 +a85 1 +#define CHF_MINOR_RELEASE_NUMBER 1 +d103 1 +d153 2 +a154 2 + const char *file_name; /* File name or CHF_UNK_FILE_NAME */ + char message[CHF_MAX_MESSAGE_LENGTH]; /* Partial message */ +d163 1 +a163 1 + char *template; /* Message template */ +d178 1 +a178 1 + const char * (*ChfMrsGet)( +d182 1 +a182 1 + const char *default_message +d199 1 +a199 1 + __FILE__, __LINE__, +d201 3 +d207 1 +a207 1 + __FILE__, __LINE__, \ +d211 1 +d219 3 +d229 1 +d243 2 +a244 2 + sigjmp_buf _chf_sigjmp_buf;\ + if(sigsetjmp(_chf_sigjmp_buf, 1) == 0)\ +d279 1 +a279 1 + const char *app_name, /* Application's name */ +d290 1 +a290 1 + const char *app_name, /* Application's name */ +d292 1 +a292 1 + const char *msgcat_name, /* Name of the message catalog */ +d299 1 +a299 1 + const char *app_name, /* Application's name */ +d308 13 +d339 1 +a339 1 +char *ChfBuildMessage( /* Build a condition message */ +d353 1 +a353 1 + const char *file_name, +d360 1 +a360 1 +const char *ChfGetMessage( /* Retrieve a condition message */ +d363 1 +a363 1 + const char *default_message +@ + + +1.6 +log +@Defined the new data type ChfPointer, a generic (void *) pointer. Each +condition handler can have a private handler context pointer, of type +ChfPointer, that the function ChfPushHandler() stores and that is passed +to the handler when it's activated. +Fixed a wrong adjustment of the condition handlers stack pointer after +an unwind operation. +@ +text +@d3 1 +a3 1 +.identifier : $Id: Chf.h,v 1.5 1996/10/04 09:45:30 cibrario Beta cibrario $ +d18 8 +d52 1 +a52 1 +#define CHF_NULL_CONTEXT (jmp_buf *)NULL +d54 5 +a58 1 +#define CHF_LIBRARY_ID "$Id: Chf.h,v 1.5 1996/10/04 09:45:30 cibrario Beta cibrario $" +d98 2 +a99 1 + CHF_UNWIND /* Stack unwind */ +d113 1 +a113 1 + CHF_SIGNALLING, +d202 23 +d282 1 +a282 1 + jmp_buf *unwind_context, /* Unwind context */ +@ + + +1.5 +log +@Updated the condition message format in the private header ChfPriv.h to +improve internationalization +@ +text +@d3 1 +a3 1 +.identifier : $Id: Chf.h,v 1.4 1996/09/25 13:21:11 cibrario Exp cibrario $ +d18 4 +d45 2 +a46 1 +#define CHF_LIBRARY_ID "$Id: Chf.h,v 1.4 1996/09/25 13:21:11 cibrario Exp cibrario $" +d126 3 +d132 2 +a133 1 + const ChfState +d246 2 +a247 1 + jmp_buf *unwind_context /* Unwind context */ +@ + + +1.4 +log +@Added macro CHF_LIBRARY_ID; it contains the current ID of the CHF library. +The module chf_init.o will contain it as a static char[] variable. +@ +text +@d3 1 +a3 1 +.identifier : $Id: Chf.h,v 1.2 1996/06/11 13:02:10 cibrario Exp cibrario $ +d18 4 +d41 1 +a41 1 +#define CHF_LIBRARY_ID "$Id$" +@ + + +1.2 +log +@Added prototype for ChfGetTopCondition() +@ +text +@d3 1 +a3 1 +.identifier : $Id: Chf.h,v 1.1 1996/05/28 12:56:47 cibrario Exp cibrario $ +d18 3 +d37 1 +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, main header +d17 3 +a19 1 + $Log$ +d21 1 +d262 4 +@ diff --git a/Chf/RCS/ChfPriv.h,v b/Chf/RCS/ChfPriv.h,v new file mode 100644 index 0000000..4f1f2e5 --- /dev/null +++ b/Chf/RCS/ChfPriv.h,v @@ -0,0 +1,298 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.11.57.57; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.14.36; author cibrario; state Rel; +branches; +next 1.6; + +1.6 +date 97.01.15.13.38.24; author cibrario; state Exp; +branches; +next 1.5; + +1.5 +date 96.10.04.09.43.51; author cibrario; state Beta; +branches; +next 1.1; + +1.1 +date 96.05.28.12.56.37; author cibrario; state Exp; +branches; +next ; + + +desc +@This is the private header of the Condition Handling Facility +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: ChfPriv.h,v 2.1 2000/05/26 14:14:36 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: ChfPriv.h,v $, main header +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 2-May-1996 +.keywords : * +.description : + This is the private header of the Condition Handling Facility + +.include : * + +.notes : + $Log: ChfPriv.h,v $ + Revision 2.1 2000/05/26 14:14:36 cibrario + - Defined new abort codes: CHF_ABORT_GET_CONTEXT, CHF_ABORT_PTHREAD + - Redefined .unwind_context field of ChfHandlerDescriptor + - Conditional retarget of chf_context for multithreading support + - Declared new private function: _ChfGetContext() (mt support only) + + Revision 1.6 1997/01/15 13:38:24 cibrario + Added the new field .handler_context to struct ChfHandlerDescriptor_S, to + store, for each condition handler, the private handler context pointer. + + Revision 1.5 1996/10/04 09:43:51 cibrario + Updated the condition message format to improve internationalization + + Revision 1.1 1996/05/28 12:56:37 cibrario + Initial revision + + +.- */ + + +/* ------------------------------------------------------------------------- + Macros + ------------------------------------------------------------------------- */ + +#define CHF_MODULE_ID CHF_SET +#define CHF_TMP_MESSAGE_LENGTH (2*CHF_MAX_MESSAGE_LENGTH) +#define CHF_DEF_MESSAGE_LENGTH 40 +#define CHF_DEF_PARTIAL_MSG_FMT ChfText("Code <%d>d") +#define CHF_DEF_MID_MSG_FMT ChfText("Mid <%d>d") +#define CHF_EXTENDED_INFO_FMT ChfText("(%s,%d)") +#define CHF_SEVERITY_NAMES \ +{ ChfText("S"), ChfText("I"), ChfText("W"), ChfText("E"), ChfText("F") } +#define CHF_UNKNOWN_SEVERITY ChfText("?") +#define CHF_MESSAGE_SEPARATOR ChfText("-") +#define CHF_MESSAGE_TERMINATOR ChfText("\n") +#define CHF_ABORT_HEADER ChfText("ChfAbort-F-") +#define CHF_ABORT_BAD_CODE_FMT ChfText("Bad abort code <%d>d\n") +#define CHF_ABORT_GOOD_CODE_FMT ChfText("%s\n") + + +/* ------------------------------------------------------------------------- + Abort codes used with ChfAbort() + ------------------------------------------------------------------------- */ + +#define CHF_ABORT_SILENT 0 +#define CHF_ABORT_INIT 1 +#define CHF_ABORT_MSG_OVF 2 +#define CHF_ABORT_INVALID_ACTION 3 +#define CHF_ABORT_DUP_INIT 4 +#define CHF_ABORT_ALREADY_UNWINDING 5 +#define CHF_ABORT_IMPROPERLY_HANDLED 6 +#define CHF_ABORT_FATAL_UNWINDING 7 +#define CHF_ABORT_COND_STACK_OVF 8 +#define CHF_ABORT_GET_CONTEXT 9 +#define CHF_ABORT_PTHREAD 10 + + + +/* ------------------------------------------------------------------------- + Type definitions + ------------------------------------------------------------------------- */ + +typedef struct ChfHandlerDescriptor_S +{ + ChfHandler handler; + void *unwind_context; + ChfPointer handler_context; +} + ChfHandlerDescriptor; + +typedef struct ChfContext_S /* CHF Context */ +{ + ChfState state; /* Current CHF state */ + const ChfChar *app_name; /* Application's name */ + ChfOptions options; /* Options */ + void *mrs_data; /* Message retrieval private data */ + ChfMrsGet mrs_get; /* 'GetMessage' function */ + ChfMrsExit mrs_exit; /* 'Exit' function */ + int condition_stack_size; /* Size of the condition stack */ + int handler_stack_size; /* Size of the handler stack */ + int exit_code; /* Abnormal exit code */ + ChfDescriptor *condition_stack; /* Condition stack */ + ChfDescriptor *condition_base; /* Current condition stack base */ + ChfDescriptor *condition_sp; /* Current condition stack pointer */ + ChfHandlerDescriptor *handler_stack; /* Handler stack */ + ChfHandlerDescriptor *handler_sp; /* Current handler stack pointer */ + ChfChar *message_buffer; /* Message buffer */ +} + ChfContext; + + +/* ------------------------------------------------------------------------- + Multithreading support + ------------------------------------------------------------------------- */ +#ifdef _REENTRANT +#define chf_context (*_ChfGetContext()) +#else +#define chf_context _chf_context +#endif + + +/* ------------------------------------------------------------------------- + Global variables + ------------------------------------------------------------------------- */ + +extern ChfContext _chf_context; /* CHF Context */ + + +/* ------------------------------------------------------------------------- + Private function prototypes + ------------------------------------------------------------------------- */ +#ifdef _REENTRANT +ChfContext *_ChfGetContext(void); +#endif + + +/* ------------------------------------------------------------------------- + Private redirection of stdlib functions needed by Win32 + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +#define ChfStrlen _tcslen +#define ChfStrcpy _tcscpy +#define ChfStrcat _tcscat +#define ChfStrncpy _tcsncpy +#define ChfSprintf _stprintf +#define ChfVsprintf _vstprintf +#else +#define ChfStrlen strlen +#define ChfStrcpy strcpy +#define ChfStrcat strcat +#define ChfStrncpy strncpy +#define ChfSprintf sprintf +#define ChfVsprintf vsprintf +#endif +@ + + +2.1 +log +@- Defined new abort codes: CHF_ABORT_GET_CONTEXT, CHF_ABORT_PTHREAD +- Redefined .unwind_context field of ChfHandlerDescriptor +- Conditional retarget of chf_context for multithreading support +- Declared new private function: _ChfGetContext() (mt support only) +@ +text +@d3 1 +a3 1 +.identifier : $Id: ChfPriv.h,v 1.6 1997/01/15 13:38:24 cibrario Exp cibrario $ +d18 6 +d45 11 +a55 10 +#define CHF_DEF_PARTIAL_MSG_FMT "Code <%d>d" +#define CHF_DEF_MID_MSG_FMT "Mid <%d>d" +#define CHF_EXTENDED_INFO_FMT "(%s,%d)" +#define CHF_SEVERITY_NAMES { "S", "I", "W", "E", "F" } +#define CHF_UNKNOWN_SEVERITY "?" +#define CHF_MESSAGE_SEPARATOR "-" +#define CHF_MESSAGE_TERMINATOR "\n" +#define CHF_ABORT_HEADER "ChfAbort-F-" +#define CHF_ABORT_BAD_CODE_FMT "Bad abort code <%d>d\n" +#define CHF_ABORT_GOOD_CODE_FMT "%s\n" +d91 1 +a91 1 + const char *app_name; /* Application's name */ +d104 1 +a104 1 + char *message_buffer; /* Message buffer */ +d132 21 +@ + + +1.6 +log +@Added the new field .handler_context to struct ChfHandlerDescriptor_S, to +store, for each condition handler, the private handler context pointer. +@ +text +@d3 1 +a3 1 +.identifier : $Id: ChfPriv.h,v 1.5 1996/10/04 09:43:51 cibrario Beta cibrario $ +d18 4 +d64 2 +d76 1 +a76 1 + jmp_buf *unwind_context; +d103 10 +d116 1 +a116 1 +extern ChfContext chf_context; /* CHF Context */ +d120 1 +a120 1 + Function prototypes +d122 3 +@ + + +1.5 +log +@Updated the condition message format to improve internationalization +@ +text +@d3 1 +a3 1 +.identifier : $Id: ChfPriv.h,v 1.1 1996/05/28 12:56:37 cibrario Exp cibrario $ +d18 3 +d71 1 +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, main header +d17 3 +a19 1 + $Log$ +d21 1 +d35 1 +a35 2 +#define CHF_SEVERITY_NAMES { "success", \ + "info", "warning", "error", "fatal" } +d37 1 +a37 1 +#define CHF_MESSAGE_SEPARATOR ": " +d39 1 +a39 1 +#define CHF_ABORT_HEADER "ChfAbort: " +@ diff --git a/Chf/RCS/Makefile,v b/Chf/RCS/Makefile,v new file mode 100644 index 0000000..5b437e6 --- /dev/null +++ b/Chf/RCS/Makefile,v @@ -0,0 +1,333 @@ +head 2.1; +access; +symbols + V4_1_1_1:2.1 + Rel_2_1:2.1; +locks; strict; +comment @# @; + + +2.1 +date 2000.05.29.13.53.07; author cibrario; state Rel; +branches; +next 1.2; + +1.2 +date 96.06.11.13.03.06; author cibrario; state Beta; +branches; +next 1.1; + +1.1 +date 96.05.29.09.12.10; author cibrario; state Exp; +branches; +next ; + + +desc +@Makefile for the CHF library +@ + + +2.1 +log +@- Deep revision to support transparent, multi-platform builds +- Added multithreading support +- New/revised targets: test, doc, clean +@ +text +@# .+ +# +# .identifier : $Id: Makefile,v 1.2 1996/06/11 13:03:06 cibrario Beta cibrario $ +# .context : CHF, Condition Handling Facility +# .title : $RCSfile: Makefile,v $, Makefile +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : 3-May-1996 +# .keywords : * +# .description : +# Makefile for the CHF library +# .notes : +# $Log: Makefile,v $ +# Revision 1.2 1996/06/11 13:03:06 cibrario +# Added new module chf_top.c +# +# Revision 1.1 1996/05/29 09:12:10 cibrario +# Initial revision +# +# +# .- + +# +# Platform-dependent definitions. Edit when required, for example to +# add a new platform. +# +# There is a set of definitions per platform; the prefix or the definition +# names is the value returned by a plain 'uname' on that platform. +# +# CC: cc compiler +# TS_CFLAGS: cc flags to enable multithreading +# TS_LOADLIBES: ld flags and additional libraries to enable multithreading +# F_CFLAGS: cc feature test macros +# O_CFLAGS: cc optimization control flags and other, additional flags +# +# Supported platforms: +# OSF1 Digital UNIX v4.0 +# SunOS Solaris v2.6 +# Linux Linux 2.1 +# + +OSF1_CC = cc +OSF1_TS_CFLAGS = -pthread +OSF1_TS_LOADLIBES = -pthread +OSF1_F_CFLAGS = "-D_POSIX_C_SOURCE=199506L -D_XOPEN_SOURCE" +OSF1_O_CFLAGS = "-O3 -std1" + +SunOS_CC = gcc +SunOS_TS_CFLAGS = -D_REENTRANT +SunOS_TS_LOADLIBES = -lpthread +SunOS_F_CFLAGS = -D_POSIX_C_SOURCE=199506L +SunOS_O_CFLAGS = "-O3 -ansi -pedantic" + +Linux_CC = cc +Linux_TS_CFLAGS = -D_REENTRANT +Linux_TS_LOADLIBES = -lpthread +Linux_F_CFLAGS = -D_POSIX_C_SOURCE=199506L +Linux_O_CFLAGS = "-O3 -ansi -pedantic" + +# +# Destination paths for library objects, C headers, binaries, message catalog +# sources and X resource files. Edit as required. +# +DESTLIBDIR = $(HOME)/lib +DESTHDRDIR = $(HOME)/include +DESTBINDIR = $(HOME)/bin +DESTMSFDIR = $(HOME)/msf +DESTRESDIR = $(HOME)/res + +# +# The default target recursively invokes make defining UNAME to the +# string returned by uname; this is used to make appropriate +# platform-dependent definitions above. +# +default: + @@$(MAKE) UNAME=`uname` all + +# +# Vectored definitions; see above and do not edit. +# +CC = $($(UNAME)_CC) +TS_CFLAGS = $($(UNAME)_TS_CFLAGS) +TS_LOADLIBES = $($(UNAME)_TS_LOADLIBES) +F_CFLAGS = $($(UNAME)_F_CFLAGS) +O_CFLAGS = $($(UNAME)_O_CFLAGS) + +# +# Include common definitions +# +include Makefile_def + +# +# Build directories for single-threaded and multi-threaded lib versions +# +ST_BUILD_DIR = ./st_build +MT_BUILD_DIR = ./mt_build + +# +# Library names +# +ST_LIB_NAME = libChf.a +MT_LIB_NAME = libChf_r.a + +# +# Library paths +# +ST_LIB_PATH = $(ST_BUILD_DIR)/$(ST_LIB_NAME) +MT_LIB_PATH = $(MT_BUILD_DIR)/$(MT_LIB_NAME) + +# +# all: Create and populate build directories; for each build directory, +# make dependencies and rebuild +# +all: $(ST_LIB_PATH) $(MT_LIB_PATH) + +# +# test: Build and executes test programs +# +test: + @@$(MAKE) UNAME=`uname` do_test + @@echo "All tests completed successfully" + +do_test: $(ST_LIB_PATH) $(MT_LIB_PATH) + @@cd $(ST_BUILD_DIR) && \ + $(MAKE) test CC=$(CC) LIB=$(ST_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) > test.log 2>&1 + @@cd $(MT_BUILD_DIR) && \ + $(MAKE) test CC=$(CC) LIB=$(MT_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) \ + TS_CFLAGS=$(TS_CFLAGS) TS_LOADLIBES=$(TS_LOADLIBES) >> test.log 2>&1 + +# +# doc: Make documentation files +# +doc: + makeinfo chf.texi + texi2dvi chf.texi; dvips -o chf.ps chf.dvi + +# +# clean: Remove build directories +# +clean: + rm -rf $(ST_BUILD_DIR) $(MT_BUILD_DIR) + +# +# install: Move all files to their directories +# +install: $(ST_LIB_PATH) $(MT_LIB_PATH) $(HDR) $(MSF) $(RES) + chmod u+w $(ST_LIB_PATH) $(MT_LIB_PATH) $(BIN) $(HDR) $(MSF) $(RES) + cp $(ST_LIB_PATH) $(MT_LIB_PATH) $(DESTLIBDIR) + cp $(HDR) $(DESTHDRDIR) + cp $(MSF) $(DESTMSFDIR) + echo cp $(RES) $(DESTRESDIR) + +# +# Prepare the build directories for use +# +$(ST_BUILD_DIR) $(MT_BUILD_DIR): + @@mkdir $@@ && \ + cd $@@ && \ + ln -s ../Makefile_sub Makefile && \ + for s in $(SRC) $(TSRC) $(HDR) $(MSF) $(TMSF) $(RES) ; do \ + ln -s ../$$s $$s ; \ + done + +# +# Make the libraries; the rebuild is forced here because only the +# sub-makes have module dependencies. +# +$(ST_LIB_PATH): $(ST_BUILD_DIR) force + @@cd $(ST_BUILD_DIR) && \ + $(MAKE) depend CC=$(CC) LIB=$(ST_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) && \ + $(MAKE) all CC=$(CC) LIB=$(ST_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) + +$(MT_LIB_PATH): $(MT_BUILD_DIR) force + @@cd $(MT_BUILD_DIR) && \ + $(MAKE) depend CC=$(CC) LIB=$(MT_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) \ + TS_CFLAGS=$(TS_CFLAGS) TS_LOADLIBES=$(TS_LOADLIBES) && \ + $(MAKE) all CC=$(CC) LIB=$(MT_LIB_NAME) \ + F_CFLAGS=$(F_CFLAGS) O_CFLAGS=$(O_CFLAGS) \ + TS_CFLAGS=$(TS_CFLAGS) TS_LOADLIBES=$(TS_LOADLIBES) + +force: +@ + + +1.2 +log +@Added new module chf_top.c +@ +text +@d3 1 +a3 1 +# .identifier : $Id: Makefile,v 1.1 1996/05/29 09:12:10 cibrario Exp cibrario $ +d15 3 +d25 37 +d63 1 +a63 1 +# sources and X resource files. +d72 9 +a80 1 +# Source files +d82 5 +a86 3 +SRC = chf_init.c chf_gen.c chf_sig.c chf_abrt.c chf_hdlr.c \ + chf_msgc.c chf_st.c \ + chf_top.c +d88 4 +a91 1 +HDR = Chf.h ChfPriv.h +d93 5 +a97 1 +MSF = chf.msf +d99 5 +a103 1 +RES = +d106 1 +a106 1 +# Targets +d108 2 +a109 3 +OBJ = $(SRC:.c=.o) +LIB = libChf.a +BIN = +d111 5 +d118 1 +a118 1 +# Other definitions +d120 3 +a122 3 +HPATH = -I. -I$(DESTHDRDIR) +LPATH = -L. -L$(DESTLIBDIR) +OFLAG = -O +d124 8 +a131 2 +CFLAGS = $(OFLAG) $(U_CFLAGS) $(HPATH) +LDFLAGS = $(LPATH) +d134 1 +a134 3 +# setenv U_LOADLIBES "-li" on Ultrix +# setenv U_LOADLIBES "" on Digital UNIX +# setenv U_LOADLIBES "-lintl" on SCO UNIX +d136 3 +a138 1 +LOADLIBES = $(LIB) $(U_LOADLIBES) +d141 1 +a141 1 +# Targets +d143 2 +a144 1 +all: $(LIB) $(BIN) +d146 6 +a151 4 +install: $(LIB) $(BIN) $(HDR) $(MSF) + chmod u+w $(LIB) $(BIN) $(HDR) $(MSF) + cp $(LIB) $(DESTLIBDIR) + echo cp $(BIN) $(DESTBINDIR) +d156 30 +a185 8 +depend: $(SRC) + makedepend $(HPATH) $(SRC) + +lint: + $(LINT) -u $(CFLAGS) $(SRC) + +$(LIB): $(OBJ) + $(AR) $(ARFLAGS) $@@ $? +d187 1 +a187 1 +test: test.c $(LIB) +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +# .identifier : $Id$ +d5 1 +a5 1 +# .title : $RCSfile$, Makefile +d14 3 +a16 1 +# $Log$ +d18 1 +d35 2 +a36 1 + chf_msgc.c chf_st.c +@ diff --git a/Chf/RCS/Makefile_def,v b/Chf/RCS/Makefile_def,v new file mode 100644 index 0000000..ece3f05 --- /dev/null +++ b/Chf/RCS/Makefile_def,v @@ -0,0 +1,116 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @# @; + + +2.2 +date 2001.01.25.11.59.19; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.29.13.08.39; author cibrario; state Rel; +branches; +next ; + + +desc +@@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@# .+ +# +# .identifier : $Id: Makefile_def,v 2.1 2000/05/29 13:08:39 cibrario Rel cibrario $ +# .context : CHF, Condition Handling Facility +# .title : $RCSfile: Makefile_def,v $, Makefile +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : 22-May-2000 +# .keywords : * +# .description : +# Makefile for CHF library common definitions; no platform dependencies here. +# .notes : +# $Log: Makefile_def,v $ +# Revision 2.1 2000/05/29 13:08:39 cibrario +# *** empty log message *** +# +# +# .- + +# +# Source files +# +SRC = chf_init.c chf_gen.c chf_sig.c chf_abrt.c chf_hdlr.c \ + chf_msgc.c chf_st.c \ + chf_top.c \ + chf_win32.c + +TSRC = test01.c test02.c test03.c test04.c test05.c test06.c \ + test07.c + +HDR = Chf.h ChfPriv.h + +MSF = chf.msf + +TMSF = test01.msf + +RES = + +# +# Target object files +# +OBJ = $(SRC:.c=.o) + +# +# Target test programs +# +TBIN = $(TSRC:.c=) + +# +# Other definitions +# +HPATH = -I. +LPATH = -L. + +CFLAGS = $(F_CFLAGS) $(O_CFLAGS) $(TS_CFLAGS) $(U_CFLAGS) $(HPATH) +LFLAGS = $(F_CFLAGS) $(L_CFLAGS) $(TS_LFLAGS) $(U_CFLAGS) $(HPATH) +LDFLAGS = $(LPATH) + +LOADLIBES = $(LIB) $(TS_LOADLIBES) $(I_LOADLIBES) $(U_LOADLIBES) + +# +# Solaris spelling of LOADLIBES +# +LDLIBS = $(LOADLIBES) +@ + + +2.1 +log +@*** empty log message *** +@ +text +@d3 1 +a3 1 +# .identifier : $Id$ +d5 1 +a5 1 +# .title : $RCSfile$, Makefile +d14 3 +a16 1 +# $Log$ +d18 1 +d26 2 +a27 1 + chf_top.c +@ diff --git a/Chf/RCS/Makefile_sub,v b/Chf/RCS/Makefile_sub,v new file mode 100644 index 0000000..6f0ccfe --- /dev/null +++ b/Chf/RCS/Makefile_sub,v @@ -0,0 +1,62 @@ +head 2.1; +access; +symbols + V4_1_1_1:2.1 + Rel_2_1:2.1; +locks; strict; +comment @# @; + + +2.1 +date 2000.05.29.13.08.51; author cibrario; state Rel; +branches; +next ; + + +desc +@@ + + +2.1 +log +@*** empty log message *** +@ +text +@# .+ +# +# .identifier : $Id$ +# .context : CHF, Condition Handling Facility +# .title : $RCSfile$, Makefile +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : 22-May-2000 +# .keywords : * +# .description : +# Makefile for CHF library subdirectories +# .notes : +# $Log$ +# +# .- + +# +# Include common definitions +# +include ../Makefile_def + +# +# Targets +# +all: $(LIB) + +$(LIB): $(OBJ) + $(AR) $(ARFLAGS) $@@ $? + +test: $(TBIN) + @@for b in $(TBIN) ; do \ + ./$$b ; \ + done + +depend: $(SRC) + @@makedepend $(HPATH) $(SRC) +@ diff --git a/Chf/RCS/chf.msf,v b/Chf/RCS/chf.msf,v new file mode 100644 index 0000000..12db6e9 --- /dev/null +++ b/Chf/RCS/chf.msf,v @@ -0,0 +1,109 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @$ . @; + + +2.2 +date 2001.01.25.12.00.19; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.17.21; author cibrario; state Rel; +branches; +next 1.1; + +1.1 +date 96.05.28.12.57.06; author cibrario; state Beta; +branches; +next ; + + +desc +@Message source file for the CHF conditions +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@$ .+ +$ . +$ .identifier : $Id: chf.msf,v 2.1 2000/05/26 14:17:21 cibrario Rel cibrario $ +$ .context : +$ .title : $RCSfile: chf.msf,v $ +$ .kind : Makefile +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 27-May-1996 +$ .keywords : * +$ .description : +$ . Message source file for the CHF conditions +$ .notes : +$ . $Log: chf.msf,v $ +$ . Revision 2.1 2000/05/26 14:17:21 cibrario +$ . Updated documentation block and RCS log message header to prevent +$ . gencat warnings on Linux boxes +$ . +$ . Revision 1.1 1996/05/28 12:57:06 cibrario +$ . Initial revision +$ . +$ .- + +$set 1 +2 Chf +3 Errno + +$set 2 +1 Condition stack is full +2 Handler stack is full +3 Handler stack is empty +4 Wrong Chf state for requested operation +5 Invalid action code from handler (code=<%d>d) +6 Dynamic memory allocation failed +7 Function not implemented +10 setlocale() failed +11 catopen() failed +@ + + +2.1 +log +@Updated documentation block and RCS log message header to prevent +gencat warnings on Linux boxes +@ +text +@d3 1 +a3 1 +$ .identifier : $Id: chf.msf,v 1.1 1996/05/28 12:57:06 cibrario Beta cibrario $ +d15 4 +d35 1 +@ + + +1.1 +log +@Initial revision +@ +text +@d2 2 +a3 2 +$ +$ .identifier : $Id$ +d5 1 +a5 1 +$ .title : $RCSfile$ +d12 1 +a12 1 +$ Message source file for the CHF conditions +d14 4 +a17 2 +$ $Log$ +$ +@ diff --git a/Chf/RCS/chf.rc,v b/Chf/RCS/chf.rc,v new file mode 100755 index 0000000..5849e50 --- /dev/null +++ b/Chf/RCS/chf.rc,v @@ -0,0 +1,123 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2; +locks; strict; +comment @# @; + + +2.2 +date 2001.01.25.12.00.46; author cibrario; state Exp; +branches; +next ; + + +desc +@@ + + +2.2 +log +@*** empty log message *** +@ +text +@//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + 1002 "Chf" +END + +STRINGTABLE DISCARDABLE +BEGIN + 2001 "Condition stack is full" + 2002 "Handler stack is full" + 2003 "Handler stack is empty" + 2004 "Wrong state for requested operation" + 2005 "Invalid action code from handler (code=<%d>d)" + 2006 "Dynamic memory allocation failed" + 2007 "Function not available" + 2010 "setlocale() failed" + 2011 "catopen() failed" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Italian (Italy) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ITA) +#ifdef _WIN32 +LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Italian (Italy) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + +@ diff --git a/Chf/RCS/chf.texi,v b/Chf/RCS/chf.texi,v new file mode 100644 index 0000000..509215d --- /dev/null +++ b/Chf/RCS/chf.texi,v @@ -0,0 +1,2677 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @# @; + + +2.2 +date 2001.01.25.12.06.29; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.29.13.02.12; author cibrario; state Rel; +branches; +next 1.6; + +1.6 +date 98.06.10.09.13.42; author cibrario; state Exp; +branches; +next ; + + +desc +@@ + + +2.2 +log +@- Added partial Win32 support (Windows CE only). +- Fixed xref and other assorted bugs. +@ +text +@\input texinfo @@c -*-texinfo-*- + +@@c $Id: chf.texi,v 2.1 2000/05/29 13:02:12 cibrario Rel cibrario $ +@@c %**start of header +@@setfilename chf.info +@@settitle Chf library (libChf r2.2) Documentation +@@setchapternewpage on +@@iftex +@@afourpaper +@@end iftex +@@c %**end of header + +@@ifinfo +This manual documents the Condition Handling Facility library (libChf r2.2). + +The Chf library provides a centralized, unified method of generating and +signaling exceptional conditions, establishing exception handlers, and +printing status and error messages. +@@end ifinfo + +@@titlepage +@@title CHF library Documentation +@@subtitle Condition Handling Facility library +@@subtitle (libChf r2.2) +@@author Ivan Cibrario B. +@@end titlepage + +@@node Top, Overview, (dir), (dir) + +@@ifinfo +This manual describes the Condition Handling Facility library, and +applies to its release 2.2. +@@end ifinfo + +@@menu +* Overview:: +* Initialization and exit:: +* Condition generation and signal:: +* General condition handling:: +* Structured condition handling:: +* Chf conditions:: +* Message retrieval:: +* Multithreading support:: +* Other useful macro definitions:: +* Other useful functions:: +* Changes since release 1:: +* Changes since release 2.1:: +* Function Index:: +* Data Type Index:: +* Concept Index:: + +@@c @@detailmenu --- The Detailed Node Listing --- + +Initialization and exit + +* ChfInit():: +* ChfMsgcatInit():: +* ChfStaticInit():: +* ChfWin32Init():: +* ChfExit():: +* ChfAbort():: +* Library header:: + +Condition generation and signal + +* Generating a condition:: +* Signaling a condition:: + +Generating a condition + +* ChfCondition:: +* ChfEnd:: +* ChfSeverity:: +* ChfErrnoCondition:: + +Signaling a condition + +* ChfSignal():: + +Condition handling + +* Condition handlers:: +* Condition descriptors:: +* Condition handling state:: +* Handler actions:: +* Default handler:: + +Condition handlers + +* ChfHandler:: +* ChfPushHandler():: +* ChfPopHandler():: + +Structured condition handling + +* Syntax of structured condition handlers:: +* Chf behavior during structured condition handling:: + +Chf behavior during structured condition handling + +* ChfTry:: +* ChfCatch:: +* ChfEndTry:: + +Message retrieval + +* Retrieving a message:: +* Reserved module identifiers:: +* Defining a new message retrieval subsystem:: + +Retrieving a message + +* ChfGetMessage():: +* ChfBuildMessage():: + +@@c @@end detailmenu +@@end menu + +@@node Overview, Initialization and exit, Top, Top +@@chapter Overview +@@cindex Overview +@@cindex Win32 Support + +The Condition Handling facility library +provides a centralized, unified method of generating and +signaling exceptional conditions. This method can be used either in +conjunction with, or in alternative to, the traditional style of handling +error conditions by returning distinguished error values when an error +occurs in a procedure, and checking for these values after each procedure +invocation. + +It serves many different purposes: + +@@itemize @@bullet + +@@item +It helps to gather and make available as much information as possible when +an error occurs, such as which source module detected it, its severity, +and any other ancillary information the user may find useful. + +@@item +It provides an unified method to log and print error messages, and to +internationalize applications. + +@@item +By means of condition handlers, it gives the user a convenient way to +implement global exception recovery actions in an application. + +@@item +It may improve the readability of the source code, because less explicit +checks for error conditions are required. + +@@end itemize + +Since release @@code{2.2}, the Chf library also supports Unicode-based Win32 +systems, notably Windows CE. Implementation details of the Win32 version +of the library will be discussed in the documentation, and will be +highlighted by the @@strong{Win32} keyword. Moreover, the following +general notes apply: + +@@itemize @@bullet + +@@item +All library functions, when compiled for Win32, handle Unicode +character strings (array of @@code{TCHAR}) instead of ASCII strings +(array of @@code{char}); this change is transparent and the documentation +does not reflect it. + +@@item +Library functions @@code{sigsetjmp()} and @@code{siglongjmp()} are not +supported; accordingly, they are replaced by +@@code{setjmp()} and @@code{longjmp()}. The non-local control transfer +context buffer, @@code{sigjmp_buf} is also replaced by @@code{jmp_buf}; +this change is transparent and the documentation does not reflect it. + +@@item +The reentrant flavor of the Chf library is not yet supported. Multithreaded +application must serialize Chf accesses themselves. + +@@item +The @@code{ChfErrnoCondition} macro is not supported, and has no effect. + +@@item +The default condition handler does not display the condition message +on the standard error stream. + +@@item +The @@code{CHF_ABORT} initialization flag is not fully supported; +when used, the Chf library invokes @@code{exit(EXIT_FAILURE)} +instead of @@code{abort()} (not supported by Win32) to abort the application. + +@@end itemize + +The following terms will be used often throughout this document: + +@@table @@code +@@item Exception +@@cindex Exception +An @@dfn{exception} is an event that changes the normal flow of instruction +execution, and is usually caused by an error condition. Any procedure can +generate an exception using the @@code{ChfGenerate} macro, and then +raise or signal it using the @@code{ChfSignal()} function call. + +@@item Condition +@@cindex Condition +An informational state which exists when an exception occurs, and encloses +information about the exception itself. The term @@dfn{condition} is preferred, +since the term exception usually implies an error. + +@@item Condition handling +@@cindex Condition handling +When a condition is detected during the execution of a procedure, the +procedure can signal the condition. The +hierarchy of currently active condition handlers is then permitted to +respond to the condition; the procedure response is called +@@dfn{condition handling}. + +The condition handlers are themselves procedures and they are called +with the usual calling conventions; the only exception is that, in the +current release of the Chf library, they cannot establish condition +handlers of their own. + +A procedure can establish a condition handler using the function +@@code{ChfPushHandler()}, that pushes a new condition handler on top of +a LIFO handler stack; the condition handler stays active until a +matching call to @@code{ChfPopHandler()}, that pops the topmost handler +from the handler stack is executed, or until an execution stack +unwind occurs. + +Another way to implicitly establishing and removing condition handlers +is to use the structured condition handling macros. +@@xref{Structured condition handling}, for more information. + +@@end table + +@@node Initialization and exit, Condition generation and signal, Overview, Top +@@chapter Initialization and exit +@@cindex Initialization and exit +@@cindex Initialization +@@cindex Exit + +Before using any function of the Chf library, it must be correctly +initialized. Since the library has a generic interface towards the +message retrieval subsystem (@@pxref{Message retrieval}), +more than one initialization function exist, one each for every message +retrieval subsystem the library supports, plus one custom initialization +function to use if a custom message retrieval subsystem is used. +After initialization, the library accesses the message retrieval subsystem +transparently, and its peculiarities are no longer visible to the users. + +All initialization functions, when successful, register a default condition +handler. @@xref{Default handler}. + +Moreover, each module using the Chf library must include the header file +@@code{Chf.h} and provide some basic definitions. @@xref{Library header}. + +@@menu +* ChfInit():: +* ChfMsgcatInit():: +* ChfStaticInit():: +* ChfWin32Init():: +* ChfExit():: +* ChfAbort():: +* Library header:: +@@end menu + +@@node ChfInit(), ChfMsgcatInit(), Initialization and exit, Initialization and exit +@@section ChfInit() +@@cindex Initialization +@@deftypefn Function int ChfInit (const char *app_name, const ChfOptions options, void *mrs_data, ChfMrsGet mrs_get, ChfMrsExit mrs_exit, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library using a custom message retrieval +subsystem, and returns to the caller a condition code; that code will be either +@@code{CHF_S_OK} if the initialization was successful, or one of the other +values listed below. + +Arguments: + +@@table @@code +@@item app_name +is the name of the application. It can be used by condition +handlers to print out error messages. + +@@item options +is the bitwise or of the following option identifiers: + +@@table @@code + +@@item CHF_DEFAULT +Represents the default value for all options. + +@@item CHF_ABORT +If this flag is set, the Chf library will use @@code{abort()} instead of +either @@code{exit(exit_code)} or @@code{pthread_exit(exit_code)} to abort +the application when an unrecoverable exception occurs. This forces a +core dump that may be useful for debugging purposes. + +@@strong{Win32:} This flag is not fully supported; the Chf library +invokes @@code{exit(EXIT_FAILURE)} instead of @@code{abort()} (not +supported by Win32) to abort the application. + +@@end table + +@@item mrs_data +Private data pointer for the custom message retrieval subsystem. + +@@item mrs_get +Message retrieval subsystem's 'Get Message' function. + +@@item mrs_exit +Message retrieval subsystem's 'Exit' function. + +@@item condition_stack_size +Size of the condition stack. This value determines the maximum number of +pending conditions, i.e. conditions that have been generated but have +not been signaled yet. A condition that is being signaled is +still pending until the signaling sequence completes. + +@@item handler_stack_size +Size of the handler stack. This value determines the maximum number of +active condition handlers the application can have. A condition handler is +activated when a @@code{ChfPushHandler()} is executed for it, and it is +deactivated when the corresponding @@code{ChfPopHandler()} completes. +In addition, each execution of the @@code{ChfTry} macro also pushes +a condition handler; the handler is removed when @@code{ChfEndTry} +is executed. + +@@item exit_code +This is the exit code the Chf library will use to exit either the +application or the offending thread when an unrecoverable exception +occurs. + +@@end table + +Return values: + +@@table @@code + +@@item CHF_S_OK +The Chf library has been successfully initialized. + +@@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@@end table + +Notes: + +@@itemize @@bullet + +@@item +It's necessary to invoke successfully @@code{ChfInit()}, either directly +or indirectly, through another Chf initialization function, before using +any other Chf function; otherwise, they will either fail and abort the +application (when multithreading support is not enabled), or have +unpredictable results (when multithreading support is enabled). + +@@item +This function will call @@code{ChfAbort()} with abort code +@@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. Notice that when multithreading support is enabled, @@code{ChfInit()} +must be invoked only once before starting multithreaded operation. + +@@end itemize +@@end deftypefn + +@@node ChfMsgcatInit(), ChfStaticInit(), ChfInit(), Initialization and exit +@@section ChfMsgcatInit() +@@cindex Initialization +@@cindex Internationalization +@@deftypefn Function int ChfMsgcatInit (const char *app_name, const ChfOptions options, const char *msgcat_name, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library and activates the message retrieval +subsystem based on @@code{catgets()}, offering support for +text-based internationalized applications. + +The argument @@code{msgcat_name} is the name of the application/locale +specific message catalog to be used to retrieve the messages. The other +arguments have the meaning already described for @@code{ChfInit()}, +@@xref{ChfInit()}. + +@@strong{Win32:} This function is not supported and always returns +@@code{CHF_F_NOT_AVAILABLE}. + +Return values: + +@@table @@code + +@@item CHF_S_OK +The Chf library has been successfully initialized. + +@@item CHF_F_SETLOCALE +the @@code{setlocale()} function has failed during initialization. + +@@item CHF_F_CATOPEN +the @@code{catopen()} function has failed and therefore the message catalog was +not opened. + +@@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@@item CHF_F_NOT_AVAILABLE +Function not available on current platform. + +@@end table + +Notes: + +@@itemize @@bullet + +@@item +It's necessary to invoke successfully either @@code{ChfMsgcatInit()}, +or another Chf initialization function described in this chapter, +before using any other Chf function. + +@@item +This function will call @@code{ChfAbort()} with abort code +@@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. + +@@end itemize +@@end deftypefn + +@@node ChfStaticInit(), ChfWin32Init(), ChfMsgcatInit(), Initialization and exit +@@section ChfStaticInit() +@@cindex Initialization +@@deftypefn Function int ChfStaticInit (const char *app_name, const ChfOptions options, const ChfTable *table, const size_t table_size, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library and activates the static +message retrieval subsystem using the message table @@code{table}, +of @@code{table_size} elements. This is the simplest message retrieval +subsystem currently supported, and supports applications that must work +with minimal operating system assistance. + +Return values: + +@@table @@code + +@@item CHF_S_OK +The Chf library has been successfully initialized. + +@@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@@end table + +Notes: + +@@itemize @@bullet + +@@item +It's necessary to invoke successfully either @@code{ChfStaticInit()}, +or another Chf initialization function described in this section, +before using any other Chf function. + +@@item +This function will call @@code{ChfAbort()} with abort code +@@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. + +@@end itemize +@@end deftypefn + +@@deftp {Data type} ChfTable +@@cindex Standalone message table +This data type is a structure representing an element of a standalone message +table, to be used with the static message retrieval subsystem. Each table +element associates a pair (@@code{module_id}, @@code{code}) with a message +fragment or template. It has the following fields: + +@@table @@code +@@item int module +The module identifier of the message fragment to be defined. + +@@item int code +The condition code of the message fragment to be defined. + +@@item char *msg_template +The message fragment or template associated with +(@@code{module_id}, @@code{code}). + +@@end table +@@end deftp + +@@node ChfWin32Init(), ChfExit(), ChfStaticInit(), Initialization and exit +@@section ChfWin32Init() +@@cindex Initialization +@@cindex Internationalization +@@deftypefn Function int ChfWin32Init (const char *app_name, const ChfOptions options, HINSTANCE instance, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library and activates the message +retrieval subsystem based on the Win32 function @@code{LoadString()}, +offering limited support for Win32 internationalized applications. + +This function does not support message sets, so the linear message id +passed to it is made by @@code{module_id*1000 + condition_code}. + +The @@code{instance} argument is the handle of the calling application +instance; it will be used as an argument to @@code{LoadString()}. All +other arguments have the meaning already described for @@code{ChfInit()}, +@@xref{ChfInit()}. + +@@strong{Win32:} This function is supported on Win32 only, and always +returns @@code{CHF_F_NOT_AVAILABLE} when invoked on Unix platforms. +Notice also that on Win32 the @@code{ChfAbort()} function will be able to +print out the abort message only if Chf has been initialized correctly +and the @@code{GetActiveWindow()} function yields a valid window handle +when @@code{ChfAbort()} is invoked. + +Return values: + +@@table @@code + +@@item CHF_S_OK +The Chf library has been successfully initialized. + +@@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@@item CHF_F_NOT_AVAILABLE +Function not available on current platform. + +@@end table + +Notes: + +@@itemize @@bullet + +@@item +It's necessary to invoke successfully either @@code{ChfWin32Init()}, +or another Chf initialization function described in this chapter, +before using any other Chf function. + +@@item +This function will call @@code{ChfAbort()} with abort code +@@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. + +@@end itemize +@@end deftypefn + +@@node ChfExit(), ChfAbort(), ChfWin32Init(), Initialization and exit +@@section ChfExit() +@@cindex Exit +@@deftypefn Function void ChfExit (void) + +This function shuts down the Chf library and returns nothing to the caller; +after calling @@code{ChfExit()} the application can continue, but any +subsequent call to other Chf functions, +except to the initialization functions, will either abort +the application using @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT} +(when multithreading support is not enabled) or have unpredictable results +(when multithreading support is enabled). + +Notes: + +@@itemize @@bullet +@@item +This function will call @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT} +if Chf hasn't been initialized yet. + +@@item +When multithreading support is enabled, it is responsibility of the +application designer to ensure that no other Chf functions, +except initialization functions, will be +called after this function has been. +@@end itemize +@@end deftypefn + +@@node ChfAbort(), Library header, ChfExit(), Initialization and exit +@@section ChfAbort() +@@cindex Abort +@@deftypefn Function void ChfAbort (const int abort_code) +This function prints the message associated with @@code{abort_code} and +then immediately aborts either the application or the invoking +thread. The abort is performed either: + +@@itemize @@bullet +@@item +using @@code{abort()} if either Chf has not been correctly initialized +yet, or the Chf option flag @@code{CHF_ABORT} has been set during +initialization. Since this is mainly intended to be a debugging aid, +@@code{abort()} is used whether or not multithreading support is enabled. +@@strong{Win32:} The @@code{CHF_ABORT} flag is not fully supported; when +this flag is set the Chf library invokes @@code{exit(EXIT_FAILURE)} instead +of @@code{abort()} (not supported by Win32) to abort the application. +@@item +using either @@code{exit(exit_code)} or @@code{pthread_exit(exit_code)} +if the above mentioned flag is clear. The former function is used when +multithreading support is not enabled, the latter when it is. The +argument @@code{exit_code} is the value of the @@code{exit_code} argument +given to the Chf initialization function. +@@end itemize + +The following abort messages are currently defined: + +@@table @@code +@@item "Not initialized" +The Chf library has not been correctly initialized. +@@item "Temporary message buffer overflow" +The temporary message buffer used by Chf to combine the additional condition +arguments with the message template was overflowed. Shorten the message. +@@item "Invalid action from last chance handler" +The condition handler registered first, that usually is the default +condition handler, has returned an invalid action code. +@@item "Already initialized" +One of the Chf initialization functions was called when Chf was already +initialized. +@@item "Unwind request while unwinding" +A condition handler has requested an unwind action when Chf was already +unwinding. This is not allowed. +@@item "Improperly handled condition" +No condition handlers were able to properly handle the current condition +group. This should not occur if the default condition handler is active +as the last chance handler. +@@item "Fatal condition while unwinding" +A fatal condition was signaled while Chf was unwinding. This is not +allowed because is would require another unwind to be recovered. +@@item "Condition stack overflow" +An attempt was made to generate a condition while the Chf condition +CHF_F_COND_STACK_FULL (condition stack full) was being signaled. +@@item "Can't prime a new Chf context" +Chf was unable to clone the master Chf context when a thread +first attempted to access its context. This message is generated +only when multithreading support is enabled. +@@item "Pthread interaction failed" +An unrecoverable error occurred when Chf invoked a @@code{pthread} +function. This message is generated only when multithreading support +is enabled. +@@end table + +No message is printed if the @@code{abort_code} is @@code{CHF_ABORT_SILENT}; +this code is used, for example, by the default condition handler to terminate +the application when a @@code{CHF_FATAL} condition occurs. + +Notes: + +@@itemize @@bullet +@@item +This function is called by other Chf library functions only when they +detect an unrecoverable failure. Even if it has a public prototype, the user +should never invoke this function. +@@item +@@strong{Win32:} Since the standard error stream is not available, this +function is able to display the abort message only if it is able to +obtain a valid main window handle, that is, if the +@@code{GetActiveWindow()} function yields a valid value when invoked. +@@end itemize + +@@end deftypefn + +@@node Library header, , ChfAbort(), Initialization and exit +@@section Library header +@@cindex Library header + +The library header @@code{Chf.h} must be included by all source modules using +the Chf library. Prior to include it, the following macro definitions must +be made: + +@@table @@code +@@item CHF_MODULE_ID +This macro must expand into an integer representing the module identifier +of the module the source file belongs. The module identifiers with value +less than 10 are reserved for Chf internal use. +@@xref{Reserved module identifiers}. + +All conditions generated by the source module will use this module +identifier. Even if this macro could in principle be redefined through the +source, it is generally inconvenient to have functions belonging to different +application's modules in the same source file, so it is discouraged. +This definition is mandatory. + +@@item CHF_EXTENDED_INFO +If this macro is set, all conditions generated by the source module +will include the file name and the line number as additional information +items. This can be useful for debugging purposes. This definition is +optional. +@@end table + +@@strong{Win32:} The @@code{tchar.h} system header must be included +before including @@code{Chf.h}. + +@@node Condition generation and signal, General condition handling, Initialization and exit, Top +@@chapter Condition generation and signal +@@cindex Condition generation and signal + +@@menu +* Generating a condition:: +* Signaling a condition:: +@@end menu + +@@node Generating a condition, Signaling a condition, Condition generation and signal, Condition generation and signal +@@section Generating a condition +@@cindex Generating a condition + +When an unusual or noteworthy condition occurs, the application may generate +a condition that describes it. + +@@menu +* ChfCondition:: +* ChfEnd:: +* ChfSeverity:: +* ChfErrnoCondition:: +@@end menu + +@@node ChfCondition, ChfEnd, Generating a condition, Generating a condition +@@subsection ChfCondition +@@deftypefn Macro {} ChfCondition {...} + +This macro starts the definition of a condition using the current +application's module specifier @@code{CHF_MODULE_ID}; it must be followed by +these additional, mandatory, arguments: + +@@table @@code +@@item int condition_code +This argument is the condition code; it uniquely identifies a condition +within the scope of the current application's module. + +@@item ChfSeverity severity +This value represents the severity of the condition. @@xref{ChfSeverity}. +@@end table + +Moreover, zero or more additional argument can follow; they will be +immediately combined with the message template corresponding to the +pair (@@code{CHF_MODULE_ID}, @@code{condition_code}) using @@code{sprintf()}. +All format specifiers of the latter function are allowed in the message +template. + +As stated before, the message template is retrieved using the message +retrieval subsystem (@@pxref{Message retrieval}) using the current Chf +module identifier @@code{CHF_MODULE_ID} as the message set, and the +condition code @@code{condition_code} as the message number. + +If in the condition stack there isn't any condition yet, a new condition group +is created, otherwise the new condition is linked on top of the current +condition group. The condition stack supports multiple nested condition +groups for use in condition handlers: any condition generated inside a +condition handler starts a new condition group that can be individually +handled and signaled. + +This function calls the Chf function @@code{ChfAbort()} if one of the following +unrecoverable error condition occurs: + +@@itemize @@bullet +@@item +Chf has not been initialized correctly (abort code @@code{CHF_ABORT_INIT}) + +@@item +there is an overflow in the internal buffer used during the +generation of the partial message associated with the condition +(abort code @@code{CHF_ABORT_MSG_OVF}) + +@@item +there was an attempt to generate a condition while the Chf condition +@@code{CHF_F_COND_STACK_FULL} (condition stack full) was being signaled +(abort code @@code{CHF_ABORT_COND_STACK_OVF}) + +@@end itemize +@@end deftypefn + + +@@node ChfEnd, ChfSeverity, ChfCondition, Generating a condition +@@subsection ChfEnd +@@deftypefn Macro {} ChfEnd + +This macro terminates the generation of the condition, started +with @@code{ChfCondition}; it must be followed by @@code{;}. For example: + +@@example +ChfCondition + , , +ChfEnd; +@@end example + +@@end deftypefn + +@@c 2.1, cibrario, 26-May-2000 +@@c Expanded description of severity codes +@@node ChfSeverity, ChfErrnoCondition, ChfEnd, Generating a condition +@@subsection ChfSeverity +@@deftp {Data type} ChfSeverity +The severity code of a condition is an enumerated type, and can assume one +of the following values: + +@@table @@code +@@item CHF_SUCCESS +This condition represents the successful completion of an application's +action; when generated in an application's module it is usually signaled +immediately after generation, it is not reported to the module caller +and does not disrupt the normal control flow of the application. + +@@item CHF_INFO +This condition conveys an information message to the application's user, +but it in no way represents an error indication; when generated in an +application's module it is usually signaled immediately after +generation, it is not reported to the module caller and does not disrupt +the normal control flow of the application. + +@@item CHF_WARNING +This condition has been generated to warn the application's user that a +potentially abnormal situation occurred, but the application's module +was able to complete successfully the action that was requested to it, +perhaps incompletely. It is usually not signaled immediately after +generation, it is reported to the caller using some status reporting +mechanism, and it requires only a minimal, local disruption of the +control flow of the application to be recovered. + +@@item CHF_ERROR +An error occurred and the application's module was not able to complete its +work, but it still may be possible to perform a local recovery and resume the +normal application's control flow. It is usually not signaled immediately +after generation and it is reported to the caller using some status +reporting mechanism. + +@@item CHF_FATAL +A fatal error occurred, and a global recovery action is required, because +the normal application's control flow can't be safely resumed. The Chf library +enforces that, when @@code{ChfSignal()} is called with a fatal condition, the +control will never return to the instruction following the @@code{ChfSignal()} +call. It is usually signaled immediately after generation. + +@@end table +@@end deftp + +@@node ChfErrnoCondition, , ChfSeverity, Generating a condition +@@subsection ChfErrnoCondition +@@deftypefn Macro {} ChfErrnoCondition + +This macro generates a condition from the current value of the @@code{errno} +variable. The module identifier of the condition is set to the special +value @@code{CHF_ERRNO_SET}. The severity is always @@code{CHF_ERROR}. + +@@strong{Win32:} This macro is not supported and has no effect when invoked. + +@@end deftypefn + + +@@node Signaling a condition, , Generating a condition, Condition generation and signal +@@section Signaling a condition +@@cindex Signaling a condition + +A condition already generated with one of the macros discussed in the +previous section can be signaled using the following function, that starts +the condition signaling sequence. + +@@menu +* ChfSignal():: +@@end menu + +@@node ChfSignal(), , Signaling a condition, Signaling a condition +@@subsection ChfSignal() +@@cindex ChfSignal() +@@deftypefn Function void ChfSignal (void) +@@end deftypefn + +This function signals the current condition group. If the final action +requested by the condition handlers invoked during the signaling operation +was @@code{CHF_CONTINUE} and the overall severity of the condition group +was lower than @@code{CHF_FATAL}, this function will return to the caller, +otherwise either a non-local control transfer will occur (@@code{CHF_UNWIND}) +or the invoking application/thread will be terminated. + +During the signaling of a condition group the following actions are +accomplished: + +@@table @@bullet +@@item +First of all, a check is made to ensure that the Chf subsystem has been +correctly initialized. If not, the function @@code{ChfAbort} is called. +@@item +The state of Chf is changed to @@code{CHF_SIGNALING} if it was @@code{CHF_IDLE}, +and to @@code{CHF_SIGNAL_UNWINDING} if it was @@code{CHF_UNWINDING}. +@@item +If there is not any active condition group, the function does nothing more, +restores the saved Chf state and returns to the caller. +@@item +If there is an active condition group, the currently active condition handlers +are invoked, starting from the one registered last. +@@item +Each condition handler can recursively invoke @@code{ChfGenerate()} and +@@code{ChfSignal()}. @@code{ChfGenerate()}, when invoked, will create a new +condition group, and @@code{ChfSignal()} will signal the condition group +just created. +@@item +When the Chf state is @@code{CHF_SIGNALING}, any new condition group generated +but not yet signaled when the current handler exits is merged +with the condition group currently being signaled, in order to allow +the condition handlers to add their own conditions to the condition +group. If the severity of the previous condition group was @@code{CHF_FATAL}, +the severity of the new group is forced to @@code{CHF_FATAL}, too. +When the Chf state is @@code{CHF_UNWINDING}, the condition group for +which the unwind has been requested is 'frozen', no further +modifications are allowed on it, and the new condition group is simply +discarded. +@@item +The condition handlers calling sequence terminates when either the handler +stack is exhausted (all condition handlers have been called) or when an +handler requests either the action @@code{CHF_CONTINUE}, +@@code{CHF_UNWIND} or @@code{CHF_UNWIND_KEEP}. +Please note that the @@code{CHF_CONTINUE} action cannot be requested if +the severity of the condition group is @@code{CHF_FATAL}; when this occurs, +the action is automatically changed to @@code{CHF_RESIGNAL} instead. +@@item +If the action @@code{CHF_CONTINUE} has been requested and it is allowed, Chf +discards the current condition group, restored the saved Chf state and +returns to the instruction following the call to @@code{ChfSignal()}, that +started the signaling activities. +@@item +If either one of the actions @@code{CHF_UNWIND} or @@code{CHF_UNWIND_KEEP} +has been requested and it is allowed, that is, +no other unwinds are already in progress, the current Chf state is set +to @@code{CHF_UNWINDING} and all handlers that were registered +after the handler that requested the unwind are called again, starting +from the one registered last, so that they can perform any final cleanup +operation they require for proper operation. The handler that requested +the unwind is called again, too, in the same fashion. Immediately after +this, the unwinded condition handlers are removed from the handler stack +and discarded, the current condition group and any other group generated +after it are destroyed, the Chf state is changed back to @@code{CHF_IDLE} +and a non-local control transfer is executed through a +@@code{siglongjmp()}, using the @@code{unwind_context} associated with the +handler that requested the unwind as target. The @@code{ChfSignal()} +invocation that started the signaling activities will never return to +the caller. If the requested action is @@code{CHF_UNWIND_KEEP} everything +goes as described above, except that the current condition group and +any other group generated after it are @@strong{not} destroyed, so they can +still be accessed after the non-local control transfer has taken place. +@@item +If no handlers were able to handle the condition, the invoking +application/thread is terminated with @@code{ChfAbort()}. +@@item +If an handler returned an invalid action code and it is not the condition +handler that was registered first, a new condition group is generated and +immediately signaled, containing the condition @@code{CHF_F_INVALID_ACTION} +with module identifier @@code{CHF_SET}. +@@end table + +@@c 2.1, cibrario, 18-May-2000 +@@c Renamed 'condition handling' -> 'general condition handling' +@@node General condition handling, Structured condition handling, Condition generation and signal, Top +@@chapter Condition handling +@@cindex Condition handling + +@@menu +* Condition handlers:: +* Condition descriptors:: +* Condition handling state:: +* Handler actions:: +* Default handler:: +@@end menu + +@@node Condition handlers, Condition descriptors, General condition handling, General condition handling +@@section Condition handlers +@@cindex Condition handlers + +The Chf library maintains a LIFO stack of active condition handlers. Each +condition handler is a function which has type @@code{ChfHandler}. +Moreover, the functions described below are available to push and pop +a condition handler to/from the condition handler stack. + +@@menu +* ChfHandler:: +* ChfPushHandler():: +* ChfPopHandler():: +@@end menu + +@@node ChfHandler, ChfPushHandler(), Condition handlers, Condition handlers +@@subsection ChfHandler +@@cindex ChfHandler +@@deftp {Data Type} ChfHandler + +The condition handler data type is defined as follows: + +@@example +typedef /* Condition handler */ + ChfAction (*ChfHandler)( + const ChfDescriptor *, + const ChfState, + ChfPointer +); +@@end example + +The condition handler receives the following arguments when invoked: + +@@table @@code + +@@item const ChfDescriptor * +is a pointer to the condition group that is being signaled. + +@@item const ChfState +represents the current state of the condition handling facility. + +@@item ChfPointer +is a copy of the @@code{handler_context} pointer passed to the +@@code{ChfPushHandler()} invocation that established the condition +handler. + +@@end table + +The handler must return an action code, represented by a value +of type @@code{ChfAction}. + +Before returning, the condition handler can generate other conditions, +without signaling them, with the purpose of better specify the current +condition group; these conditions will be added on top of the current +group when the handler returns. The next handler, if any, will be invoked +on the updated condition group. + +The severity @@code{CHF_FATAL} is 'sticky', that is, any new condition +linked to a condition group where the topmost condition has that +severity will be forced to the same severity level. + +If the condition handler itself generates and signals a new condition +group, its superior condition handlers will be invoked on the new +condition group only. When the new signaling sequence is completed, +the old sequence will be resumed if appropriate, i.e. if a superior +handler has requested the @@code{CHF_CONTINUE} action and the severity +of the new condition group allows it to do so. + +@@end deftp + + +@@node ChfPushHandler(), ChfPopHandler(), ChfHandler, Condition handlers +@@subsection ChfPushHandler() +@@cindex Condition handlers +@@cindex Adding condition handlers +@@deftypefn Function void ChfPushHandler (ChfHandler new_handler, void *unwind_context, ChfPointer handler_context) + +This function pushes the new condition handler @@code{new_handler} with +its associated @@code{siglongjmp()} context, pointed by +@@code{unwind_context}, into the handler stack. If @@code{new_handler} is +@@code{CHF_NULL_HANDLER}, the special builtin structured condition +handling helper is pushed; see @@ref{Structured condition handling}, for +further information. + +Moreover, this function saves a copy of the opaque pointer +@@code{handler_context}; it will be passed to @@code{new_handler} upon each +subsequent activation, and therefore can be used as a private handler context +pointer. The user must ensure that the information pointed by +@@code{handler_context}, if any, will remain valid until @@code{new_handler} +is popped from the condition stack. + +The pointer @@code{handler_context} may be set to the special (null) value +@@code{CHF_NULL_POINTER} to indicate that the handler hasn't any private +context information. + +If, in the future, the handler will request the @@code{CHF_UNWIND} +action, the @@code{sigsetjmp()} function invocation that established +@@code{unwind_context} will appear to return again. + +The pointer @@code{unwind_context} can be the reserved (null) pointer +@@code{CHF_NULL_CONTEXT}; in this case, if the handler will request the +@@code{CHF_UNWIND} action, the invoking application/thread will be +silently terminated calling @@code{ChfAbort()} with abort code +@@code{CHF_ABORT_SILENT}. + +If an error occurs during the execution, the function generates +and immediately signals one of the conditions listed below; the function +will never return directly to the caller, since all conditions have +severity @@code{CHF_FATAL}. + +@@table @@code + +@@item CHF_F_BAD_STATE +Bad Chf state for requested operation. A new handler can be added to the +handler stack only if the condition handling subsystem is idle. + +@@item CHF_F_HDLR_STACK_FULL, +The handler stack is full and the new handler can't be added. + +@@end table + +Notes: + +@@itemize @@bullet + +@@item +This function will call @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT} +if Chf hasn't been initialized. + +@@end itemize +@@end deftypefn + +@@node ChfPopHandler(), , ChfPushHandler(), Condition handlers +@@subsection ChfPopHandler() +@@cindex Condition handlers +@@cindex Removing condition handlers +@@deftypefn Function void ChfPopHandler (void) + +This function pops the topmost condition handler from the handler stack and +returns to the caller. + +If an error occurs during the execution, the function generates +and immediately signals one of the conditions listed below; the function +will never return directly to the caller, since all conditions have +severity @@code{CHF_FATAL}. + +@@table @@code + +@@item CHF_F_BAD_STATE +Bad Chf state for requested operation. Handlers can be removed from the +handler stack only if the condition handling subsystem is idle. + +@@item CHF_F_HDLR_STACK_EMPTY +The handler stack is empty. + +@@end table + +Notes: + +@@itemize @@bullet + +@@item +This function will call @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT} +if Chf hasn't been initialized. + +@@end itemize + +@@end deftypefn + + +@@node Condition descriptors, Condition handling state, Condition handlers, General condition handling +@@section Condition descriptors +@@cindex Condition descriptors +@@deftp {Data Type} ChfDescriptor + +The condition descriptor contains all information Chf has about a +condition code. Its fields must not be accessed directly, but using +the macros the Chf library provides for this purpose. + +@@end deftp + +The following macros are provided to access a condition descriptor: + +@@deftypefn Macro {ChfDescriptor *} ChfGetNextDescriptor (Chf Descriptor *d) +@@cindex Next condition descriptor + +Returns to the caller the condition descriptor that hierarchically +follows @@code{d} in the current condition group, or the special +value @@code{CHF_NULL_DESCRIPTOR} if @@code{d} is the last descriptor of the +group. + +@@end deftypefn + +@@deftypefn Macro int ChfGetModuleId (Chf Descriptor *d) +@@cindex Module identifier + +Returns to the caller the identifier of the module that generated +the condition @@code{d}. + +@@end deftypefn + +@@deftypefn Macro int ChfGetConditionCode (Chf Descriptor *d) +@@cindex Condition Code + +Returns to the caller the condition code of the condition @@code{d}. + +@@end deftypefn + +@@deftypefn Macro ChfSeverity ChfGetSeverity (Chf Descriptor *d) +@@cindex Severity + +Returns to the caller the severity level of the condition @@code{d}. +@@xref{ChfSeverity}. + +@@end deftypefn + +@@deftypefn Macro int ChfGetLineNumber (Chf Descriptor *d) +@@cindex Line number + +Returns to the caller the source line number where the condition @@code{d} +was generated. This piece of information is available only if the +Chf compile-time option @@code{CHF_EXTENDED_INFO} was set when the source +file that generated the condition was compiled, otherwise the special +value @@code{CHF_UNKNOWN_LINE_NUMBER} is returned instead. + +@@end deftypefn + +@@deftypefn Macro {const char *} ChfGetFileName (Chf Descriptor *d) +@@cindex File name + +Returns to the caller the file name where the condition @@code{d} +was generated. This piece of information is available only if the +Chf compile-time option @@code{CHF_EXTENDED_INFO} was set when the source +file that generated the condition was compiled, otherwise the special +value @@code{CHF_UNKNOWN_FILE_NAME} is returned instead. + +@@end deftypefn + +@@deftypefn Macro {char *} ChfGetPartialMessage (Chf Descriptor *d) +@@cindex Partial condition message + +Returns to the caller the partial condition message that was associated +with the condition @@code{d} when it was generated. The partial condition +message contains the result of the execution of @@code{sprintf()} using +the message template associated with the condition as format, and the +ancillary condition arguments as additional arguments. + +@@end deftypefn + + + +@@node Condition handling state, Handler actions, Condition descriptors, General condition handling +@@section Condition handling state +@@cindex Condition handling state +@@deftp {Data Type} ChfState +This enumerated type describes the current state of the Condition Handling +Facility. It can have one of the following values: + +@@table @@code +@@item CHF_UNKNOWN +The Chf library has not been initialized, or the initialization has failed. +Normally, the user never sees this value, because the main use of +@@code{ChfState} values is inside condition handlers, and they are never +invoked when the Chf library has not been initialized correctly. + +@@item CHF_IDLE +This is the normal state of the condition handling facility. In this state, +the condition stack contains zero or more condition descriptors corresponding +to conditions that have been generated but have not been signaled yet. +The application follows its normal control flow. + +@@item CHF_SIGNALING +The Condition Handling Facility is invoking the hierarchy of active +condition handlers because @@code{ChfSignal()} has been invoked with a +non-empty condition stack. None of the condition handlers invoked so far +has requested an unwind operation. + +@@item CHF_UNWINDING +The Condition Handling Facility is unwinding one or more condition handlers, +because another, superior, handler has requested an unwind operation. +All handlers being unwinded are called once with Chf in this state to allow +them to perform any final cleanup operation, and immediately after that +they are removed from the condition stack. During unwind, +the action code returned by the handler being unwinded is ignored. + +@@item CHF_SIGNAL_UNWINDING +While Chf was performing an unwind operation, one of the handlers being +unwinded has signaled another condition group. The superiors of the +signaling handler are invoked with Chf in this state, then the unwind +operation is resumed. + +@@end table +@@end deftp + +@@node Handler actions, Default handler, Condition handling state, General condition handling +@@section Handler actions +@@cindex Handler actions +@@deftp {Data Type} ChfAction + +Enumerated type representing the set of mutually exclusive actions that a +condition handler can request to the condition handling facility. It can +have the following values: + +@@table @@code + +@@item CHF_CONTINUE +Resume the application from the instruction immediately following the +@@code{ChfSignal()} call that triggered the invocation of the condition handler. +This action is not allowed if the severity of the current condition is +@@code{CHF_FATAL}, since this severity always requires global recovery +to take place. In this case the @@code{CHF_CONTINUE} is automatically +changed to @@code{CHF_RESIGNAL} when encountered. + +@@item CHF_RESIGNAL +Invoke the next handler in the handler stack with the same condition. +This action must be used when the current condition handler was not able +to recover the condition. + +@@item CHF_UNWIND +Unwind the application's stack and restore the @@code{unwind_context} +associated with the condition handler, performing a non-local control +transfer. @@xref{ChfPushHandler()}. + +@@c 2.1, cibrario, 18-May-2000 +@@c Added descruption of CHF_UNWIND_KEEP +@@item CHF_UNWIND_KEEP +Unwind the application's stack and restore the @@code{unwind_context} +associated with the condition handler, performing a non-local control +transfer. Unlike @@code{CHF_UNWIND}, the current condition group +at the time of the unwind is @@strong{not} destroyed, +and can still be accessed after the non-local control transfer has +taken place. +@@end table +@@end deftp + +@@node Default handler, , Handler actions, General condition handling +@@section Default handler +@@cindex Default handler + +The default condition handler of Chf is automatically +pushed into the condition handler stack by the Chf initialization +functions. It performs the following functions: + +@@table @@bullet +@@item +if called during an unwind, it returns immediately to the caller, +requesting the action @@code{CHF_RESIGNAL}, else + +@@item +if the severity of the condition being signaled is greater than +@@code{CHF_SUCCESS}, it prints the messages associated with the entire +condition group on stderr using the standard function +@@code{ChfBuildMessage()} to build the messages. +@@strong{Win32: } Since the standard error stream is not available, +the default handler does not print anything out. + +@@item +if the severity of the condition being signaled is less than +@@code{CHF_FATAL}, it returns to the caller requesting the action +@@code{CHF_CONTINUE}, else + +@@item +if the @@code{CHF_FATAL} condition was @@strong{not} signaled during an unwind +operation, it returns to the caller requesting the action +@@code{CHF_UNWIND}, otherwise it requests the action @@code{CHF_RESIGNAL}. + +@@end table + +@@c 2.1, cibrario, 18-May-2000 +@@c New chapter: Structured condition handling +@@node Structured condition handling, Chf conditions, General condition handling, Top +@@chapter Structured condition handling +@@cindex Structured condition handling + +Structured condition handling places the condition handler associated +with a block of code next to the block itself, instead of into a separate +procedure. Even if it is less flexible and powerful with respect to the +general handling method described before, it is often clearer and +simpler to implement. Chf supports both styles of condition handling; +the structured condition handling is layered above the general one. + +@@menu +* Syntax of structured condition handlers:: +* Chf behavior during structured condition handling:: +@@end menu + +@@node Syntax of structured condition handlers, Chf behavior during structured condition handling, Structured condition handling, Structured condition handling +@@section Syntax of structured condition handlers +@@cindex Syntax of structured condition handlers + +The syntax establishing a structured condition handler is as follows: + +@@example +ChfTry + @@{ + ... body ... + @@} +ChfCatch + @@{ + const ChfDescriptor *exc = ChfGetTopCondition(); + ... condition handler ... + @@} +ChfEndTry; +@@end example + +The macros @@code{ChfTry}, @@code{ChfCatch} and @@code{ChfEndTry} +expand as follows: + +@@example +<< +@@{ + sigjmp_buf _chf_sigjmp_buf; + if(sigsetjmp(_chf_sigjmp_buf, 1) == 0) + @@{ + ChfPushHandler(CHF_NULL_HANDLER, _chf_sigjmp_buf, CHF_NULL_POINTER); +>> + @@{ + ... body ... + @@} +<< + ChfPopHandler(); + @@} + else + @@{ +>> + @@{ + const ChfDescriptor *exc = ChfGetTopCondition(); + ... condition handler ... + @@} +<< + ChfDiscard(); + @@} +@@} +>> +@@end example + +Above, @@code{<<} and @@code{>>} mark the starting and ending point of +each macro expansion, respectively. + +The @@code{body} is either a statement or a block of statements (a block +is shown in the example) to be protected. If an exception with severity +equal to @@code{CHF_FATAL} is signaled during the execution of +@@code{body}, control is transferred to the @@code{condition handler} +following @@code{ChfCatch}. + +The handler can then retrieve the condition group associated with the +exception using @@code{ChfGetTopCondition()}, as shown in the example, +and must perform one of the following actions: + +@@itemize @@bullet +@@item +Handle the exception; when the condition handler completes, that is, its +last statement has been executed, the current condition group is +automatically discarded and control is transferred to the code that +follows @@code{ChfEndTry}. +@@item +Resignal the exception, possibly after adding one or more conditions to +it; in this case, the handler explicitly calls @@code{ChfSignal()}. +In response, Chf continues to search for a handler that actually +handles the exception, in the @@code{ChfTry}/@@code{ChfCatch} blocks +enclosing the current one, in inner to outer order. The control +will never be returned to the resignaling handler. +@@end itemize + +Notice that it is not possible for the condition handler to resume +the thread of execution disrupted by the exception. Resuming is +possible only when the signaled condition has severity less than +@@code{CHF_FATAL}: in this case the @@code{condition handler} is +not invoked, the usual condition signaling sequence takes place +and the @@code{CHF_CONTINUE} action requested by any general +condition handler is honored. + +Thus, structured condition handling is further simplified because: + +@@itemize @@bullet +@@item +Conditions with severity less than @@code{CHF_WARNING} are usually +automatically signaled immediately after generation and are not reported +to the caller; they are informational conditions only, and they must not +disrupt the normal control flow of the application. Accordingly, the +structured condition handler is @@strong{not} invoked when a +success/informational condition is signaled in its @@code{ChfTry} body. +@@item +@@code{CHF_WARNING} conditions generated by a module are usually not +signaled immediately after generation, but are reported to the +caller using some status reporting mechanism. When a warning +condition is generated by a module, it indicates that its execution +has been at least partially successful; this usually means that +the disruption of the control flow of the application can be kept to +a minimum and handled locally. Accordingly, the structured condition +handler is @@strong{not} invoked when a warning condition +is signaled in its @@code{ChfTry} body. +@@item +@@code{CHF_ERROR} conditions generated by a module indicate that the +module execution has been unsuccessful; they are usually reported to the +caller using some status reporting mechanisms. An error condition +indicates that a significant disruption of the control flow of the +application is required to recover, and that local handling is still +possible but could not suffice. Accordingly, the structured condition +handler is @@strong{not} invoked when an error condition is signaled in +its @@code{ChfTry} body. Notice also that if the @@code{ChfTry} body is +not able to locally recover the condition, it can add a @@code{CHF_FATAL} +condition to the condition group and signal the modified group, thus +triggering the invocation of the structured condition handler. +@@item +@@code{CHF_FATAL} conditions are usually signaled immediately after +generation and always require a global recovery action. +Accordingly, the structured condition handler is +invoked when a warning condition is signaled in its @@code{ChfTry} body. +@@end itemize + +Notice also that control is transferred to the structured exception +handler by means of a @@code{siglongjmp()} sequence. Therefore, +if the structured exception handler needs to access automatic +variables, they should be declared @@code{volatile}. + +If no handlers are able to handle an exception, the default Chf +condition handler is invoked last. @@xref{Default handler}, for a more +detailed description of its operation. + +If no exceptions are raised during the execution of @@code{body}, +the @@code{condition handler} is skipped and execution continues +immediately after @@code{ChfEndTry}. + +@@node Chf behavior during structured condition handling, , Syntax of structured condition handlers, Structured condition handling +@@section Chf behavior during structured condition handling +@@cindex Chf behavior during structured condition handling + +When a condition is signaled and the special structured condition +handling helper is invoked, the following sequence of events occurs: + +@@itemize @@bullet +@@item +Chf transitions to state @@code{CHF_SIGNALING} and starts +the handler calling sequence. +@@item +If any handler invoked before the structured condition handling helper +requests either the @@code{CHF_CONTINUE} or the @@code{CHF_UNWIND} +action, that action is executed as usual. +@@item +When invoked, the structured condition handling helper immediately +requests the action @@code{CHF_UNWIND_KEEP}. +@@item +Chf transitions to state @@code{CHF_UNWINDING} and invokes all handlers +registered after the structured condition handling helper again. Such +handlers can then perform whatever final cleanup operations they need. +@@item +Chf invokes the structured condition handling helper again; the helper +immediately returns without doing anything, since Chf is now in state +@@code{CHF_UNWINDING}. +@@item +Chf transitions to the @@code{CHF_IDLE} state and performs a non-local +control transfer to the structured condition handler's unwind context; +this corresponds to the code that follows @@code{ChfCatch}. +The current condition group is not removed from the condition stack. +@@item +Since the current condition group has not been removed from the +condition stack, the code that follows @@code{ChfCatch} can freely +access the condition group using @@code{ChfGetTopCondition()} +and @@code{ChfGetNextDescriptor()}. +@@item +If any new condition is generated in this phase, it is merged with the +current condition group; the condition group can be re-signaled at will +by invoking @@code{ChfSignal()}. +@@item +Immediately after the code that follows @@code{ChfCatch} completes, +@@code{ChfDiscard()} is automatically invoked to remove the topmost +condition group from the stack. +@@end itemize + +If the structured condition handling helper is invoked when Chf is in +@@code{CHF_SIGNAL_UNWINDING}, it immediately requests the +@@code{CHF_RESIGNAL} action, because a further unwind request is neither +allowed nor useful in this case. + +The structured condition handling helper makes no use of +@@code{handler_context}. + +In summary, the following structured condition handling macros +are currently available: + +@@menu +* ChfTry:: +* ChfCatch:: +* ChfEndTry:: +@@end menu + +@@node ChfTry, ChfCatch, Chf behavior during structured condition handling, Chf behavior during structured condition handling +@@subsection ChfTry +@@deftypefn Macro {} ChfTry {...} + +This macro introduces a new structured condition handling body. +The body is either a statement or a block of statements to be +protected: if a @@code{CHF_FATAL} exception is signaled during its +execution, control is transferred to the condition handling code +following @@code{ChfCatch}. + +@@end deftypefn + +@@node ChfCatch, ChfEndTry, ChfTry, Chf behavior during structured condition handling +@@subsection ChfCatch +@@deftypefn Macro {} ChfCatch {...} + +This macro must follow @@code{ChfTry} and introduces a new structured +condition handler following it; the handler can be either a statement or +a block of statements. If a @@code{CHF_FATAL} exception is signaled +during the execution of the body protected by @@code{ChfTry}, control is +transferred to the condition handling code following @@code{ChfCatch}. + +@@end deftypefn + +@@node ChfEndTry, , ChfCatch, Chf behavior during structured condition handling +@@subsection ChfEndTry +@@deftypefn Macro {} ChfEndTry + +This macro marks the end of a body/condition handler pair. + +@@end deftypefn + + +@@node Chf conditions, Message retrieval, Structured condition handling, Top +@@chapter Chf conditions +@@cindex Chf conditions + +The Chf library can itself generate and signal the following conditions. +All conditions are fatal, i.e. require a global recovery action to take +place to be handled. + +@@deftypefn Macro {} CHF_F_COND_STACK_FULL +The user attempted to generate a condition while the condition stack was full; +this condition is generated anyway using a reserved stack entry. +No additional conditions can be generated until the handling of this condition +is completed, otherwise the application will be aborted. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_HDLR_STACK_FULL +The function @@code{ChfPushHandler()} was called while the handler stack +was full; the new handler is not registered and this condition is signaled +using the old handler hierarchy. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_HDLR_STACK_EMPTY +A function requiring at least one condition handler to be active +found an empty handler stack. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_BAD_STATE +The current state of Chf was not appropriate to invoke a certain +Chf function. The invoked function does not do anything but +signaling this condition. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_INVALID_ACTION +During the current signaling sequence an handler returned an invalid +action code. A new condition group, containing only this condition, is +generated and immediately signaled. The first handler called to handle +the new condition group will be the handler registered immediately before +the faulting one. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_MALLOC +Many Chf functions dynamically allocate memory. This condition is generated +and signaled when a dynamic memory allocation attempt failed, and the library +was unable to recover in another way. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_NOT_AVAILABLE +The function is not available on the current platform. This condition +is returned by some Chf initialization functions instead of being +signaled, because it prevents the Chf subsystem to be initialized and, +therefore, to function properly. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_SETLOCALE +The @@code{setlocale()} function failed during the initialization of the +message catalog-based message retrieval subsystem. This condition is returned +by the Chf initialization function instead of being signaled, because it +prevents the Chf subsystem to be initialized and, therefore, to function +properly. +@@end deftypefn + +@@deftypefn Macro {} CHF_F_CATOPEN +The @@code{catopen()} function failed during the initialization of the +message catalog-based message retrieval subsystem. This condition is returned +by the Chf initialization function instead of being signaled, because it +prevents the Chf subsystem to be initialized and, therefore, to function +properly. +@@end deftypefn + + +@@node Message retrieval, Multithreading support, Chf conditions, Top +@@chapter Message retrieval +@@cindex Message retrieval +The message retrieval subsystem is responsible to retrieve the message +fragments needed by the Chf library to generate the messages associated +with condition codes. + +Basically, given a message set identifier (that usually has the same value +as the module identifier for which the condition is being generated) and +a message number (that usually has the same value as the condition code), +it must retrieve and return to the caller a suitable message associated +to it. It the message cannot be found, it must return a default message +pointer Chf provides. + +Some module identifiers/message sets are used for special purposes. +@@xref{Reserved module identifiers}. + +@@menu +* Retrieving a message:: +* Reserved module identifiers:: +* Defining a new message retrieval subsystem:: +@@end menu + +@@node Retrieving a message, Reserved module identifiers, Message retrieval, Message retrieval +@@section Retrieving a message +The following functions directly interact with the message retrieval subsystem: + +@@menu +* ChfGetMessage():: +* ChfBuildMessage():: +@@end menu + +@@node ChfGetMessage(), ChfBuildMessage(), Retrieving a message, Retrieving a message +@@subsection ChfGetMessage() +@@deftypefn Function {const char *} ChfGetMessage (const int module_id, const int condition_code, const char *default_message) + +This function transparently uses the Chf message retrieval subsystem to +retrieve the message associated with the pair +(@@code{module_id}, @@code{condition_code}) and returns a pointer to it. +The function will return @@code{default_message} instead, if it was not able +to retrieve the message. If @@code{module_id==CHF_ERRNO_SET}, the function +will additionally invoke @@code{strerror()}, if necessary, to retrieve the +message. + +@@strong{Win32:} Since @@code{strerror()} is not supported, the additional +last-chance translation described above cannot be performed. + +Notes: + +@@itemize @@bullet + +@@item +The returned pointer points to per-thread static storage, which can be +overwritten by subsequent calls to this function. + +@@end itemize +@@end deftypefn + + +@@node ChfBuildMessage(), , ChfGetMessage(), Retrieving a message +@@subsection ChfBuildMessage() +@@deftypefn Function {char *} ChfBuildMessage (const ChfDescriptor *descriptor) + +This function builds the message associated with the given condition +descriptor and returns a pointer to a string containing it. + +The message has a standard format and provides the following information +items: + +@@itemize @@bullet +@@item +The application's name given to the Chf library during initialization, if +the condition is the topmost within a condition group; otherwise, the message +starts with an horizontal tab character. +@@item +The name of the module that generated the condition. +@@item +If available, the file name and line number of the source where the condition +was generated. +@@item +The severity code of the message. +@@item +The message associated with the condition code, including any ancillary +information provided during condition generation. +@@end itemize + +Notes: + +@@itemize @@bullet + +@@item +This function will call @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT} +if Chf hasn't been correctly initialized. + +@@item +The returned pointer points to per-thread static storage, which can be +overwritten by subsequent calls to this function. + +@@item +@@strong{Win32:} to save space, the application's name and severity code +are not included in the message. + +@@end itemize +@@end deftypefn + +@@node Reserved module identifiers, Defining a new message retrieval subsystem, Retrieving a message, Message retrieval +@@section Reserved module identifiers +@@cindex Reserved module identifiers + +The following module identifiers are currently used by the Chf library; +all module identifiers with value less than 10 are reserved for future use. + +@@table @@code +@@item 1 +This module identifier is used as a message set identifier to retrieve the +names of the other application's modules. In particular, to retrieve the +name of the application's module @@code{m}, the message @@code{m} of +message set @@code{1} is retrieved. + +@@item 2 +This module identifier is used to identify the conditions generated by +the Chf library itself. + +@@item 3 +This module identifier is used as a message set identifier to retrieve the +messages associated with the @@code{errno} error codes. If the retrieve is +unsuccessful, @@code{strerror()} is used instead. +@@end table + +@@strong{Win32:} In addition, the current implementation of the +message retrieval subsystem on Win32 imposes the following limits: +@@code{0 <= condition_code <= 999}, @@code{0 <= module_id <= 64}. + + +@@c 2.1, cibrario, 18-May-2000 +@@c Completed section on defining a new mrs +@@node Defining a new message retrieval subsystem, , Reserved module identifiers, Message retrieval +@@section Defining a new message retrieval subsystem +@@cindex Defining a new message retrieval subsystem + +The Chf library has a generic interface towards the message retrieval +subsystem (MRS); therefore, any subsystem can be used, provided it follows +the guidelines described below: + +@@itemize @@bullet +@@item +A copy of the @@code{mrs_data} argument of @@code{ChfInit()} is passed to +each MRS function when it is invoked. It can be used to hold a pointer +to a private MRS data structures. +@@item +The @@code{mrs_get} function has the following arguments: + @@itemize @@bullet + @@item + A copy of the @@code{mrs_data} argument of @@code{ChfInit()}. + @@item + An @@code{int}, representing the module identifier of the condition message + template/fragment to be retrieved. + @@item + An @@code{int}, representing the condition code of the condition message + template/fragment to be retrieved. + @@item + A @@code{char *}, pointing to a default message template/fragment to + be returned if the message retrieval fails. + @@end itemize +The function must attempt to retrieve the message template/fragment +associated with the pair (module identifier, condition code) and return +it to the caller; if the retrieval fails, the default message must be +returned instead. +@@item +The @@code{mrs_exit} function is invoked when Chf is about to exit. +It has as argument a copy of the @@code{mrs_data} argument or +@@code{ChfInit()} and must perform any cleanup operation the MRS +needs. It is guaranteed that no other MRS function will be called +after a successful invocation of @@code{mrs_exit}. +@@item +When multithreading support is enabled, the MRS must support multiple, +concurrent invocations made by different application's threads. The same +value of @@code{mrs_data} is common to all threads and cannot be changed +after initialization. +@@end itemize + +@@c 2.1, cibrario, 18-May-2000 +@@c New chapter on multithreading support; revised all other chapters to +@@c take multithreading support into account, too. +@@node Multithreading support, Other useful macro definitions, Message retrieval, Top +@@chapter Multithreading support +@@cindex Multithreading support + +When compiled appropriately, the Chf library supports multithreading, +and maintains one Chf context per active thread. Multithreading support +is enabled when the cpp macro @@code{_REENTRANT} is defined. + +@@strong{Win32:} Multitreading support is not provided yet. + +When multithreading support is enabled, the following modifications +to the default Chf behavior come into effect: + +@@itemize @@bullet +@@item +@@code{ChfInit()} no longer initializes the condition stack, the +condition handler stack and the message buffer immediately; instead, one +copy of these context elements will be allocated for each active thread +in the application, when it first invokes any other Chf function. +Also, per-thread Chf contexts are dynamically destroyed when the +owning thread terminates. +@@item +Condition generation and both general and structured condition handling +work as before inside each application's thread. All threads share the +same values for @@code{condition_stack_size}, @@code{handler_stack_size} +and @@code{exit_code}, but are otherwise fully independent with +respect to condition handling, because each of them has a private +Chf context. +@@item +@@code{ChfAbort()} no longer exits the whole application when +the @@code{CHF_ABORT} Chf flag not set; instead, it exits the +invoking thread only. +@@item +Almost all Chf functions can trigger the @@code{CHF_ABORT_GET_CONTEXT} +and @@code{CHF_ABORT_PTHREAD} aborts, if they fail to correctly +manage dynamic Chf context creation and/or @@code{pthread} interactions. +@@item +All Chf functions, except @@code{ChfInit()} and @@code{ChfExit()} +no longer check whether Chf has been correctly initialized or not: +that check would have been too time-consuming in a multithreaded environment. +As a consequence, invoking a Chf function when Chf has not been correctly +initialized has unpredictable effects instead of neatly aborting +the application with abort code @@code{CHF_ABORT_INIT}. +@@item +The performance of the Chf condition generation and signaling functions +is likely to decrease, because Chf must retrieve its per-thread context +dynamically instead of referring to a static storage area. +@@end itemize + +@@node Other useful macro definitions, Other useful functions, Multithreading support, Top +@@chapter Other useful macro definitions +@@cindex Other useful macro definitions + +@@deftypefn Macro {} CHF_MAX_MESSAGE_LENGTH +Represents the maximum allowable length for condition messages. +@@end deftypefn + +@@deftypefn Macro {} CHF_LIBRARY_ID +It is a string representing the Chf library identifier; the identifier +contains also the library release number. +@@end deftypefn + +@@c 2.1, cibrario, 18-May-2000 +@@c New macros +@@deftypefn Macro {} CHF_MAJOR_RELEASE_NUMBER +It is an integer representing the Chf library major release number. +@@end deftypefn + +@@deftypefn Macro {} CHF_MINOR_RELEASE_NUMBER +It is an integer representing the Chf library minor release number. +@@end deftypefn + +@@deftypefn Macro {} CHF_MODULE_NAMES_SET +Contains the integer identifier of the message set whose messages are used to +retrieve the names of the application's modules. It has the value 1. +@@end deftypefn + +@@deftypefn Macro {} CHF_SET +Contains the integer identifier of the message set containing the condition +messages Chf uses internally. It has the value 2. +@@end deftypefn + +@@deftypefn Macro {} CHF_ERRNO_SET +Contains the integer identifier of the message set containing the condition +messages for @@code{errno} codes. It has the value 3. +@@end deftypefn + +@@deftypefn Macro {} ChfChar +This macro is used to make portable declarations of character variables, +and is mapped into either @@code{char} or @@code{TCHAR} depending on the +platform. +@@end deftypefn + +@@deftypefn Macro {} ChfText (x) +This macro is used to make portable declarations of character strings, +and is mapped into either @@code{x} or @@code{_T(x)} depending on the +platform. +@@end deftypefn + +@@deftypefn Macro {} ChfSigjmp_buf +This macro is mapped into either @@code{sigjmp_buf} or @@code{jmp_buf}, +depending on whether the platform supports @@code{sigjmp_buf} +contexts or not. +@@end deftypefn + +@@deftypefn Macro {} ChfSigsetjmp (x,y) +This macro is mapped into either @@code{sigsetjmp()} or @@code{setjmp()}, +depending on whether the platform supports @@code{sigjmp_buf} +contexts or not. In the latter case argument @@code{x} is ignored. +@@end deftypefn + +@@deftypefn Macro {} ChfSiglongjmp (x,y) +This macro is mapped into either @@code{siglongjmp()} or @@code{longjmp()}, +depending on whether the platform supports @@code{sigjmp_buf} +contexts or not. +@@end deftypefn + +@@node Other useful functions, Changes since release 1, Other useful macro definitions, Top +@@chapter Other useful functions +@@cindex Other useful functions + +@@deftypefn Function void ChfDiscard (void) +This function discards the topmost condition group currently in the +condition stack, without signaling it. The function does nothing if +the condition stack is empty. + +This function uses the function @@code{ChfAbort()} to +abort the application if Chf has not been initialized correctly +(abort code @@code{CHF_ABORT_INIT}). +@@end deftypefn + +@@deftypefn Function {const ChfDescriptor *} ChfGetTopCondition (void) +This function returns to the caller a pointer to the topmost condition of +the current condition group. It generates and immediately signals the +condition @@code{CHF_F_BAD_STATE} if the current condition group is empty. + +This function can be useful to retrieve the complete description of a +condition for functions that, when an error occurs, generate a condition +and return to the caller a generic error indication, such as a distinguished +return value. + +The same function can also be useful in a structured exception handler, +to retrieve the condition descriptor associated with the exception to +be handled. + +Notes: + +@@itemize @@bullet +@@item +During condition signaling, Chf creates a new, empty, condition group +immediately before starting the invocation sequence of the condition +handlers, as previously described. Therefore +@@code{ChfGetTopCondition()}, when called from a condition handler, will return +a pointer to the top condition generated during the handling ONLY, if any, and +NOT to the top condition of the condition group being signaled. The +latter pointer is directly available, as an argument, to condition +handlers. +@@item +When called from a structured exception handler, @@code{ChfGetTopCondition()} +works as expected, that is, it returns a pointer to the top condition of +the condition group associated with the exception being handled. +@@item +This function will call @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT} +if Chf hasn't been correctly initialized. +@@item +The returned pointer is no longer valid when any other Chf function +is called after @@code{ChfGetTopCondition()}. +@@end itemize +@@end deftypefn + +@@c 2.1, cibrario, 18-May-2000 +@@c New chapter on changes since release 1 +@@node Changes since release 1, Changes since release 2.1, Other useful functions, Top +@@chapter Changes since release 1 +@@cindex Changes since release 1 + +@@itemize @@bullet +@@item +Chf now supports structured condition handling. +@@xref{Structured condition handling}. +@@item +Chf now supports multithreaded applications; each thread has its own Chf +context. Multithreading support is conditionally enabled by linking a +multithreaded version of the Chf library. +@@xref{Multithreading support}. +@@item +Chf now supports the conditional save and restore of signal masks in unwind +contexts (@@code{sigjmp_buf} contexts instead of @@code{jmp_buf}). +@@item +The method of passing unwind contexts as arguments is now consistent +with that used by @@code{sigsetjmp()}; in particular, it is no longer +necessary to reference the contexts with @@code{&}. +@@item +Some more useful macro definitions have been added to @@code{Chf.h}. +@@xref{Other useful macro definitions}. +@@end itemize + +@@c 2.2, cibrario, 25-Jan-2001 +@@c New chapter on changes since release 2.1 +@@node Changes since release 2.1, Function Index, Changes since release 1, Top +@@chapter Changes since release 2.1 +@@cindex Changes since release 2.1 + +@@itemize @@bullet +@@item +Chf now offers limited support of Unicode-based Win32 platforms, +notably Windows CE. +@@end itemize + +@@node Function Index, Data Type Index, Changes since release 2.1, Top +@@unnumbered Function Index +@@printindex fn + +@@node Data Type Index, Concept Index, Function Index, Top +@@unnumbered Data Type Index +@@printindex tp + +@@c @@node Variable Index, Concept Index, Data Type Index, Top +@@c @@unnumbered Variable Index +@@c @@printindex vr + +@@node Concept Index, , Data Type Index, Top +@@unnumbered Concept Index +@@printindex cp + +@@contents + +@@bye +@ + + +2.1 +log +@Updated documentation for libChf release 2.1: +- Fixed many typos and spelling errors +- Updated documentation to explain multithreading support +- Documented new abort codes/messages related to multithreading support +- Expanded description of severity codes +- Documented new ChfAction code CHF_UNWIND_KEEP +- Changed data type jmp_buf -> sigjmp_buf; documented new parameter passing + policy for unwind contexts +- New chapter: Structured condition handling +- Finished documententing the message retrieval subsystem +- New macros in Chf.h: CHF_MAJOR_RELEASE_NUMBER and CHF_MINOR_RELEASE_NUMBER +- Documented changes since release 1 +@ +text +@d3 1 +a3 1 +@@c $Id: chf.texi,v 1.6 1998/06/10 09:13:42 cibrario Exp cibrario $ +d6 1 +a6 1 +@@settitle Chf library (libChf r2.1) Documentation +d14 1 +a14 1 +This manual documents the Condition Handling Facility library (libChf r2.1). +d24 1 +a24 1 +@@subtitle (libChf r2.1) +d32 1 +a32 1 +applies to its release 2.1. +d47 1 +d59 1 +d122 1 +d126 1 +a126 1 +signaling exceptional conditions. This method can used either in +d142 1 +a142 1 +It provides a unified method to log and print error messages, and to +d155 5 +d161 33 +d255 1 +a255 1 +@@code{Chf.h} and provide some basic definitions. @@xref{Library header} +d261 1 +d298 4 +d382 3 +d402 3 +d424 1 +a424 1 +@@node ChfStaticInit(), ChfExit(), ChfMsgcatInit(), Initialization and exit +d478 1 +a478 1 +@@item char *template +d485 58 +a542 1 +@@node ChfExit(), ChfAbort(), ChfStaticInit(), Initialization and exit +d560 1 +a560 1 +if CHF hasn't been initialized. +d564 2 +a565 1 +application designer to ensure that no other Chf functions will be +d584 3 +d643 5 +d665 1 +a665 1 +@@xref{Reserved module identifiers} +d680 3 +d720 1 +a720 1 +This value represents the severity of the condition. @@xref{ChfSeverity} +d730 1 +a730 1 +retrieval subsystem, @@xref{Message retrieval}, using the current Chf +d741 1 +a741 1 +This function calls the CHF function @@code{ChfAbort()} if one of the following +d833 2 +d871 1 +a871 1 +The state of Chf is changed to @@code{CHF_SIGNALLING} if it was @@code{CHF_IDLE}, +d885 1 +a885 1 +When the Chf state is @@code{CHF_SIGNALLING}, any new condition group generated +d898 2 +a899 1 +handler requests either the action @@code{CHF_CONTINUE} or @@code{CHF_UNWIND}. +d993 5 +d1065 1 +a1065 1 +Bad CHF state for requested operation. A new handler can be added to the +d1079 1 +a1079 1 +if CHF hasn't been initialized. +d1101 1 +a1101 1 +Bad CHF state for requested operation. Handlers can be removed from the +d1115 1 +a1115 1 +if CHF hasn't been initialized. +d1164 1 +a1164 1 +@@xref{ChfSeverity} +d1223 1 +a1223 1 +@@item CHF_SIGNALLING +d1259 1 +a1259 1 +@@code{ChfSignal()} that caused the invocation of the condition handler. +d1273 1 +a1273 1 +transfer. @@xref{ChfPushHandler()} +d1305 2 +d1432 2 +a1433 2 +structured condition handler is @@strong{not} invoked when a warning +condition is signaled in its @@code{ChfTry} body. +d1457 1 +a1457 1 +@@code{CHF_FATAL} condition are usually signaled immediately after +d1485 1 +a1485 1 +Chf transitions to state @@code{CHF_SIGNALLING} and starts +d1514 1 +a1514 1 +current condition group; the condition group can be resignalled at will +d1565 1 +a1565 1 +@@deftypefn Macro {} ChfEndTry; +d1618 7 +d1657 1 +a1657 1 +@@xref{Reserved module identifiers} +d1686 3 +d1734 1 +a1734 1 +if CHF hasn't been correctly initialized. +d1740 4 +d1771 5 +d1790 1 +a1790 1 +to a private MRS data structured. +d1834 2 +d1913 30 +d1998 1 +a1998 1 +@@node Changes since release 1, Function Index, Other useful functions, Top +d2023 13 +a2035 1 +@@node Function Index, Data Type Index, Changes since release 1, Top +@ + + +1.6 +log +@*** empty log message *** +@ +text +@d3 1 +a3 1 +@@c $Id$ +d6 1 +a6 1 +@@settitle Chf library (libChf r1.6) Documentation +d14 1 +a14 1 +This manual documents the Condition Handling Facility library (libChf r1.6). +d17 1 +a17 1 +signalling exceptional conditions, establishing exception handlers, and +d24 1 +a24 1 +@@subtitle (libChf r1.6) +d32 1 +a32 1 +applies to its release 1.6. +d39 2 +a40 1 +* Condition handling:: +d43 1 +d46 1 +d49 1 +a49 1 +* Concept Index:: @@c @@detailmenu +d65 1 +a65 1 +* Signalling a condition:: +d74 1 +a74 1 +Signalling a condition +d92 11 +d123 1 +a123 1 +signalling exceptional conditions. This method can used either in +d126 1 +a126 1 +occours in a procedure, and checking for these values after each procedure +d135 1 +a135 1 +an error occours, such as which source module detected it, its severity, +d165 1 +a165 1 +An informational state which exists when an exception occours, and encloses +d186 6 +a191 1 +from the handler stack is executed. +d210 2 +a211 2 +All initialization functions, when succesful, register a default condition +handler. @@xref{Default handler} +d232 1 +a232 1 +@@code{CHF_S_OK} if the initialization was succesful, or one of the other +d252 3 +a254 3 +@@code{exit(exit_code)} to abort the application when an unrecoverable +exception occours. This forces a core dump that may be useful for +debugging purposes. +d268 1 +a268 1 +Size of the condition stack. This value fixes the maximum number of +d270 2 +a271 2 +not been signalled yet. A condition that is begin signalled is +still pending until the signalling sequence completes. +d274 1 +a274 1 +Size of the handler stack. This value fixes the maximum number of +d278 3 +d283 3 +a285 2 +This is the exit code the Chf library will use to exit the application +when an unrecoverable exception occours. +d294 1 +a294 1 +The Chf library has been succesfully initialized. +d306 1 +a306 1 +It's necessary to invoke succesfully @@code{ChfInit()}, either directly +d308 3 +a310 1 +any other Chf function; otherwise, they will fail and abort the application. +d315 2 +a316 1 +before. +d341 1 +a341 1 +The Chf library has been succesfully initialized. +d360 1 +a360 1 +It's necessary to invoke succesfully either @@code{ChfMsgcatInit()}, +d362 1 +a362 2 +before using any other Chf function; otherwise, any other +function will fail and abort the application. +d388 1 +a388 1 +The Chf library has been succesfully initialized. +d400 1 +a400 1 +It's necessary to invoke succesfully either @@code{ChfStaticInit()}, +d402 1 +a402 2 +before using any other Chf function; otherwise, any other +function will fail and abort the application. +d441 4 +a444 2 +except to the inizialization functions, will abort +the application using @@code{ChfAbort()} with abort code @@code{CHF_ABORT_INIT}. +a448 1 + +d453 4 +d464 3 +a466 2 +This function prints the message associated with @@code{abort_code} and then +immediately aborts the application. The abort is performed either: +d472 2 +a473 1 +initialization. +d475 4 +a478 2 +using @@code{exit(exit_code)} if the above mentioned flag is clear. +The argument @@code{exit_code} is the value of the @@code{exit_code} argument +d501 1 +a501 1 +group. This should not occour if the default condition handler is active +d504 1 +a504 1 +A fatal condition was signalled while Chf was unwinding. This is not +d508 9 +a516 1 +CHF_F_COND_STACK_FULL (condition stack full) was being signalled. +d521 1 +a521 2 +the application when a @@code{CHF_FATAL} condition occours. + +d552 1 +a552 1 +appication's modules in the same source file, so it is discouraged. +d562 1 +a562 1 +@@node Condition generation and signal, Condition handling, Initialization and exit, Top +d568 1 +a568 1 +* Signalling a condition:: +d571 1 +a571 1 +@@node Generating a condition, Signalling a condition, Condition generation and signal, Condition generation and signal +d575 1 +a575 1 +When an unusual or noteworthy condition occours, the appliction may generate +d618 1 +a618 1 +handled and signalled. +d620 2 +a621 2 +This function calls the CHF function 'ChfAbort()' if one of the following +unrecoverable error condition occours: +d634 1 +a634 1 +@@code{CHF_F_COND_STACK_FULL} (condition stack full) was being signalled +d656 2 +d666 4 +a669 1 +This condition represents the succesful completion of an application's action. +d672 5 +a676 2 +This condition conveys an information message to the application's user, but +it in no way represents an error indication. +d680 6 +a685 3 +potentially abnormal situation occoured, but the application's module +was able to complete succesfully the action that was requested to it, +perhaps incompletely. +d688 5 +a692 3 +An error occoured and the application's module was not able to complete its +work, but it may be possible to perform a local recovery and resume the +normal application's control flow. +d695 1 +a695 1 +A fatal error occoured, and a global recovery action is required, because +d699 1 +a699 1 +call. +d715 3 +a717 3 +@@node Signalling a condition, , Generating a condition, Condition generation and signal +@@section Signalling a condition +@@cindex Signalling a condition +d720 2 +a721 2 +previous section can be signalled using the following function, that starts +the condition signalling sequence. +d727 1 +a727 1 +@@node ChfSignal(), , Signalling a condition, Signalling a condition +d734 1 +a734 1 +requested by the condition handlers invoked during the signalling operation +d737 2 +a738 2 +otherwise either a non-local control transfer will occour (@@code{CHF_UNWIND}) +or the application's process will be terminated. +d740 1 +a740 1 +During the signalling of a condition group the following actions are +d745 2 +a746 2 +First of all, a check is make to ensure that the Chf subsystem has been +correctly initialized. If not, the funcion @@code{ChfAbort} is called. +d763 2 +a764 2 +but not yet signalled when the current handler exits is merged +with the condition group currently being signalled, in order to allow +d774 1 +a774 1 +stack is exausted (all condition handlers have been called) or when an +d777 1 +a777 1 +the severity of the condition group is @@code{CHF_FATAL}; when this occours, +d783 1 +a783 1 +started the signalling activities. +d785 2 +a786 1 +If the action @@code{CHF_UNWIND} has been requested and it is allowed, i.e. +d790 1 +a790 1 +from the one registered last, this way they can perform any final cleanup +d792 12 +a803 8 +the unwind is called again, too, in the same fashion. +Immediately after this, the unwinded condition handlers are removed from +the handler stack and discarded, the current condition group and any other +group generated after it are destroyed, the Chf state is changed back to +@@code{CHF_IDLE} and a non-local control transfer is executed through a +@@code{longjmp()}, using the @@code{unwind_context} associated with the handler +that requested the unwind as target. The @@code{ChfSignal()} invocation that +started the signalling activities will never return to the caller. +d805 2 +a806 2 +If no handlers were able to handle the condition, the application is +terminated with @@code{ChfAbort()}. +d810 1 +a810 1 +immediately signalled, containing the condition @@code{CHF_F_INVALID_ACTION} +d814 3 +a816 1 +@@node Condition handling, Chf conditions, Condition generation and signal, Top +d828 1 +a828 1 +@@node Condition handlers, Condition descriptors, Condition handling, Condition handling +d832 1 +a832 1 +The Chf library mantains a LIFO stack of active condition handlers. Each +d864 1 +a864 1 +is a pointer to the condition group that is being signalled. +d867 1 +a867 1 +repesents the current state of the condition handling facility. +d875 1 +a875 1 +without signalling them, with the purpose of better specify the current +d886 2 +a887 2 +condition group only. When the new signalling sequence is completed, +the old sequence will be resumed if sppropriate, i.e. if a superior +d898 1 +a898 1 +@@deftypefn Function void ChfPushHandler (ChfHandler new_handler, jmp_buf *unwind_context, ChfPointer handler_context) +d900 6 +a905 3 +This function pushes the new condition handler @@code{new_handler} with its +associated longjmp context, pointed by @@code{unwind_context}, into the handler +stack. +d918 3 +a920 3 +If, in the future, the handler will request the @@code{CHF_UNWIND} action, +the @@code{setjmp()} function invocation that established @@code{unwind_context} +will appear to return again. +d924 3 +a926 2 +@@code{CHF_UNWIND} action, the application will be silently terminated +calling @@code{ChfAbort()} with abort code @@code{CHF_ABORT_SILENT}. +d928 1 +a928 1 +If an error occours during the execution, the function generates +d964 1 +a964 1 +If an error occours during the execution, the function generates +d993 1 +a993 1 +@@node Condition descriptors, Condition handling state, Condition handlers, Condition handling +d1043 1 +a1043 1 +was generated. This piece of information is avaliable only if the +d1054 1 +a1054 1 +was generated. This piece of information is avaliable only if the +d1074 1 +a1074 1 +@@node Condition handling state, Handler actions, Condition descriptors, Condition handling +d1083 1 +a1083 1 +The Chf library has not been initializes, or the initialization has failed. +d1091 1 +a1091 1 +to conditions that have been generated but have not been signalled yet. +d1110 2 +a1111 2 +unwinded has signalled another condition group. The superiors of the +signalling handler are invoked with Chf in this state, then the unwind +d1117 1 +a1117 1 +@@node Handler actions, Default handler, Condition handling state, Condition handling +d1146 9 +d1158 1 +a1158 1 +@@node Default handler, , Handler actions, Condition handling +d1163 1 +a1163 1 +pushed into the condition handler stack by the Chf initalization +d1172 1 +a1172 1 +if the severity of the condition being signalled is greater than +d1178 1 +a1178 1 +if the severity of the condition being signalled is less than +d1183 1 +a1183 1 +if the @@code{CHF_FATAL} condition was NOT signalled during an unwind +d1189 246 +d1436 6 +a1441 1 +@@node Chf conditions, Message retrieval, Condition handling, Top +d1452 1 +a1452 1 +No additional condition can be generated until the handling of this condition +d1458 1 +a1458 1 +was full; the new handler is not registered and this condition is signalled +d1469 2 +a1470 2 +Chf function. The invoked function does not do anything and signals but +signalling this condition. +d1474 1 +a1474 1 +During the current signalling sequence an handler returned an invalid +d1476 1 +a1476 1 +generated and immediately signalled. The first handler called to handle +d1483 1 +a1483 1 +and signalled when a dynamic memory allocation attempt failed, and the library +d1490 1 +a1490 1 +by the Chf initialization function instead of being signalled, because it +d1498 1 +a1498 1 +by the Chf initialization function instead of being signalled, because it +d1504 1 +a1504 1 +@@node Message retrieval, Other useful macro definitions, Chf conditions, Top +d1511 1 +a1511 1 +Basically, given a message set identifier (that generally has the same value +d1513 1 +a1513 1 +a message number (that generally has the same value as the condition code), +d1540 1 +a1540 1 +This function transparently uses the Chf message retrieval subystem to +d1553 1 +a1553 1 +The returned pointer points to static storage, which can be +d1583 1 +a1583 1 +The message associated with the condition code, comprising any ancillary +d1596 1 +a1596 1 +The returned pointer points to static storage, which can be +d1623 1 +a1623 1 +unsuccesful, @@code{strerror()} is used instead. +d1626 2 +d1630 1 +d1633 39 +a1671 2 +subsystem; therefore, any subsystem can be used, provided it follows +the guidelines described below. +d1673 10 +a1682 1 +This section of the documentation is still TBD (libChf r1.6) +d1684 38 +d1723 1 +a1723 1 +@@node Other useful macro definitions, Other useful functions, Message retrieval, Top +d1732 1 +a1732 1 +It is a string repesenting the Chf library identifier; the identifier +d1736 10 +d1761 1 +a1761 1 +@@node Other useful functions, Function Index, Other useful macro definitions, Top +d1767 1 +a1767 1 +condition stack, without signalling it. The function does nothing if +d1781 1 +a1781 1 +condition for functions that, when an error occours, generate a condition +d1785 4 +d1793 1 +a1793 1 +During condition signalling, Chf creates a new, empty, condition group +d1798 1 +a1798 1 +NOT to the top condition of the condition group being signalled. The +d1802 4 +d1814 28 +a1841 1 +@@node Function Index, Data Type Index, Other useful functions, Top +@ diff --git a/Chf/RCS/chf_abrt.c,v b/Chf/RCS/chf_abrt.c,v new file mode 100644 index 0000000..8592f9a --- /dev/null +++ b/Chf/RCS/chf_abrt.c,v @@ -0,0 +1,312 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.12.08.24; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.22.07; author cibrario; state Rel; +branches; +next 1.1; + +1.1 +date 96.05.28.12.53.26; author cibrario; state Beta; +branches; +next ; + + +desc +@This module implements the CHF function ChfAbort() +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_abrt.c,v 2.1 2000/05/26 14:22:07 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_abrt.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the CHF function ChfAbort() + +.include : Chf.h + +.notes : + $Log: chf_abrt.c,v $ + Revision 2.1 2000/05/26 14:22:07 cibrario + - Conditional inclusion of pthread.h (mt support) + - Expanded abort message table with mt support messages + - ChfAbort() with CHF_ABORT flag clear and mt support enabled now + exits invoking thread only + + Revision 1.1 1996/05/28 12:53:26 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_abrt.c,v 2.1 2000/05/26 14:22:07 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + +#ifdef _REENTRANT +#include +#endif + + +/* Abort codes message table; the relative position of the messages must + match the numeric codes CHF_ABORT_xxxx defined in ChfPriv.h +*/ +static const ChfChar *message_table[] = +{ + (const ChfChar *)NULL, + ChfText("Not initialized"), + ChfText("Temporary message buffer overflow"), + ChfText("Invalid action from last chance handler"), + ChfText("Already initialized"), + ChfText("Unwind request while unwinding"), + ChfText("Improperly handled condition"), + ChfText("Fatal condition while unwinding"), + ChfText("Condition stack overflow"), + ChfText("Can't prime a new Chf context"), + ChfText("Pthread interaction failed") +}; + +#define MESSAGE_TABLE_SIZE (sizeof(message_table)/sizeof(const ChfChar *)) + + +/* .+ + +.title : ChfAbort +.kind : C function +.creation : 13-May-1996 +.description : + This function prints the message associated with 'abort_code' and then + immediately aborts either the application (when multithreading support not + enabled or CHF_ABORT set) or the invoking thread only (multithreading + support enabled and CHF_ABORT not set). The abort is performed either: + - using abort() if either CHF has not been correctly initialized or + the chf_context.options flag CHF_ABORT is set + - using exit(chf_context.exit_code) (multithreading support not enabled) + or pthread_exit(chf_context.exit_code) (multithreading support enabled) + if the flag is clear + + No message is printed if the abort code is CHF_ABORT_SILENT; this code + is used, for example, by the default condition handler to terminate the + application when a CHF_FATAL condition occours. + + NOTE: This function must be called only when either a serious internal CHF + failure occurs or it's necessary to abort the application. + + WIN32: + + - stderr stream is not supported; the abort message is displayed in a + message box only if Chf has been correctly initialized, otherwise the + abort will be done silently + + - abort() is not supported and has been replaced by exit(EXIT_FAILURE) + +.call : + ChfAbort(abort_code); +.input : + const int abort_code, abort_code +.output : + void +.status_codes : + none +.notes : + 1.1, 13-May-1996, creation + 2.1, 19-May-2000, update: + - added multithreading support + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +void ChfAbort( /* Abort application */ + const int abort_code +) +{ +#ifdef _WIN32 + if(abort_code != CHF_ABORT_SILENT) + { + TCHAR abort_msg[CHF_MAX_MESSAGE_LENGTH]; + HWND active_window; + + /* stderr not available; + put complaint in a message box and display it + */ + if(abort_code < 0 || abort_code >= MESSAGE_TABLE_SIZE) + _stprintf(abort_msg, + CHF_ABORT_BAD_CODE_FMT, abort_code); + + else + _stprintf(abort_msg, + CHF_ABORT_GOOD_CODE_FMT, message_table[abort_code]); + + /* Return value of MessageBox() ignored, because there is only + one available choice (abort) here. Avoid using a NULL handle. + */ + if(chf_context.state != CHF_UNKNOWN + && (active_window = GetActiveWindow()) != (HWND)NULL) + (void) + MessageBox(active_window, + abort_msg, + chf_context.app_name, + MB_OK + |MB_ICONERROR + |MB_APPLMODAL|MB_SETFOREGROUND); + } + + /* Immediately exit the application with exit code EXIT_FAILURE + if CHF_ABORT option is set or if something is wrong with Chf state. + */ + if(chf_context.state == CHF_UNKNOWN || chf_context.options & CHF_ABORT) + exit(EXIT_FAILURE); + + else + /* Else, exit the application anyway, but with the exit code + registered by the application. Don't use PostQuitMessage(), + because the contract is that ChfAbort() never returns to the caller. + */ +#ifndef _REENTRANT + exit(chf_context.exit_code); +#else +#error "_REENTRANT not supported yet" +#endif + +#else + if(abort_code != CHF_ABORT_SILENT) + { + fputs(CHF_ABORT_HEADER, stderr); + + if(abort_code < 0 || abort_code >= MESSAGE_TABLE_SIZE) + fprintf(stderr, CHF_ABORT_BAD_CODE_FMT, abort_code); + + else + fprintf(stderr, CHF_ABORT_GOOD_CODE_FMT, message_table[abort_code]); + } + + if(chf_context.state == CHF_UNKNOWN || chf_context.options & CHF_ABORT) + abort(); + + else +#ifndef _REENTRANT + exit(chf_context.exit_code); +#else + pthread_exit((void *)(chf_context.exit_code)); +#endif +#endif +} +@ + + +2.1 +log +@- Conditional inclusion of pthread.h (mt support) +- Expanded abort message table with mt support messages +- ChfAbort() with CHF_ABORT flag clear and mt support enabled now + exits invoking thread only +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_abrt.c,v 1.1 1996/05/28 12:53:26 cibrario Beta cibrario $ +d18 6 +d31 1 +a31 1 +static char rcs_id[] = "$Id: chf_abrt.c,v 1.1 1996/05/28 12:53:26 cibrario Beta cibrario $"; +d36 1 +d38 1 +d41 5 +d57 1 +a57 1 +static const char *message_table[] = +d59 11 +a69 11 + (const char *)NULL, + "Not initialized", + "Temporary message buffer overflow", + "Invalid action from last chance handler", + "Already initialized", + "Unwind request while unwinding", + "Improperly handled condition", + "Fatal condition while unwinding", + "Condition stack overflow", + "Can't prime a new Chf context", + "Pthread interaction failed" +d72 1 +a72 1 +#define MESSAGE_TABLE_SIZE (sizeof(message_table)/sizeof(const char *)) +d96 9 +a104 1 + failure occours or it's necessary to abort the application. +d118 2 +d126 49 +d195 1 +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 3 +a19 1 + $Log$ +d21 1 +d25 1 +a25 1 +static char rcs_id[] = "$Id$"; +d36 4 +d54 3 +a56 1 + "Condition stack overflow" +d69 3 +a71 1 + immediately aborts the application. The abort is performed either: +d74 3 +a76 1 + - using exit(chf_context.exit_code) if the flag is clear +d95 2 +d118 1 +d120 3 +@ diff --git a/Chf/RCS/chf_gen.c,v b/Chf/RCS/chf_gen.c,v new file mode 100644 index 0000000..dc72761 --- /dev/null +++ b/Chf/RCS/chf_gen.c,v @@ -0,0 +1,253 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:1.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.12.10.22; author cibrario; state Exp; +branches; +next 1.1; + +1.1 +date 96.05.28.12.53.59; author cibrario; state Rel; +branches; +next ; + + +desc +@This module contains the condition generation function of CHF +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_gen.c,v 1.1 1996/05/28 12:53:59 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_gen.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module contains the condition generation function of CHF + +.include : Chf.h + +.notes : + $Log: chf_gen.c,v $ + Revision 1.1 1996/05/28 12:53:59 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_gen.c,v 1.1 1996/05/28 12:53:59 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* .+ + +.title : ChfGenerate +.kind : C function +.creation : 3-May-1996 +.description : + This function generates a condition descriptor for the condition described + by 'module_id', 'file_name', 'line_number', 'condition_code' and 'severity', + and the partial message associated with it, specialized with the additional + arguments '...'; the new condition descriptor is put onto the top of the + condition stack. + + If the condition stack is full, this function generates the condition + CHF_F_COND_STACK_FULL and immediately invokes ChfSignal() for the current + condition stack. + + NOTE: This function calls the CHF function 'ChfAbort()' to + abort the application if either: + - CHF has not been initialized correctly (abort code CHF_ABORT_INIT) + - there is an overflow in the internal buffer used during the + generation of the partial message associated with the condition + (abort code CHF_ABORT_MSG_OVF) + - there was an attempt to generate a condition while the CHF condition + CHF_F_COND_STACK_FULL (condition stack full) was being signalled + (abort code CHF_ABORT_COND_STACK_OVF) + + +.call : + ChfGenerate(module_id, file_name, line_number, + condition_code, severity, ...); +.input : + const int module_id, module identifier + const char *file_name, file name + const int line_number, line number + const int condition_code, condition code + const ChfSeverity severity, severity + ..., additional arguments +.output : + void +.status_codes : + (*) CHF_F_COND_STACK_FULL, the condition stack is full +.notes : + 1.1, 3-May-1996, creation + +.- */ +void ChfGenerate( /* Generate a condition into the stack */ + const int module_id, + const ChfChar *file_name, + const int line_number, + const int condition_code, + const ChfSeverity severity, + ... +) +{ + ChfDescriptor *new_descriptor; + va_list aux_arg; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + /* Prepare the additional arguments list */ + va_start(aux_arg, severity); + + if((new_descriptor = chf_context.condition_sp) - + chf_context.condition_stack >= chf_context.condition_stack_size) + { + /* The condition stack is full; + generate the CHF_F_COND_STACK_FULL condition and signal it immediately, + using the last available slot of the stack, if it's still empty, + otherwise abort the application. + */ + if(new_descriptor - chf_context.condition_stack == + chf_context.condition_stack_size) + { + new_descriptor->module_id = CHF_MODULE_ID; + new_descriptor->condition_code = CHF_F_COND_STACK_FULL; + new_descriptor->severity = CHF_FATAL; + new_descriptor->line_number = CHF_UNKNOWN_LINE_NUMBER; + new_descriptor->file_name = CHF_UNKNOWN_FILE_NAME; + + ChfStrncpy(new_descriptor->message, + ChfGetMessage(CHF_MODULE_ID, CHF_F_COND_STACK_FULL, + ChfText("Condition stack is full")), CHF_MAX_MESSAGE_LENGTH-1); + new_descriptor->message[CHF_MAX_MESSAGE_LENGTH-1] = '\0'; + + new_descriptor->next = CHF_NULL_DESCRIPTOR; + chf_context.condition_sp++; + + ChfSignal(); + } + + else + ChfAbort(CHF_ABORT_COND_STACK_OVF); + } + + else + { + ChfChar def_message[CHF_DEF_MESSAGE_LENGTH]; + ChfChar tmp_message[CHF_TMP_MESSAGE_LENGTH]; + + new_descriptor->module_id = module_id; + new_descriptor->condition_code = condition_code; + new_descriptor->severity = severity; + new_descriptor->line_number = line_number; + new_descriptor->file_name = file_name; + + /* Generate the default message */ + ChfSprintf(def_message, CHF_DEF_PARTIAL_MSG_FMT, condition_code); + + /* Generate the partial message associated with the condition using a + temporary area + */ + if( + ChfVsprintf(tmp_message, + ChfGetMessage(module_id, condition_code, def_message), aux_arg) >= + CHF_TMP_MESSAGE_LENGTH) + ChfAbort(CHF_ABORT_MSG_OVF); + + /* Copy the message into the condition descriptor */ + ChfStrncpy(new_descriptor->message, tmp_message, CHF_MAX_MESSAGE_LENGTH-1); + new_descriptor->message[CHF_MAX_MESSAGE_LENGTH-1] = '\0'; + + /* Link the new descriptor with the current descriptor list, if it + isn't the first descriptor of the list + */ + new_descriptor->next = (new_descriptor == chf_context.condition_base) + ? CHF_NULL_DESCRIPTOR : new_descriptor - 1; + + chf_context.condition_sp++; + } +} +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 3 +a19 1 + $Log$ +d21 1 +d25 1 +a25 1 +static char rcs_id[] = "$Id$"; +d30 1 +d32 1 +d37 5 +d93 1 +a93 1 + const char *file_name, +d126 1 +a126 1 + strncpy(new_descriptor->message, +d128 1 +a128 1 + "Condition stack is full"), CHF_MAX_MESSAGE_LENGTH-1); +d143 2 +a144 2 + char def_message[CHF_DEF_MESSAGE_LENGTH]; + char tmp_message[CHF_TMP_MESSAGE_LENGTH]; +d153 1 +a153 1 + sprintf(def_message, CHF_DEF_PARTIAL_MSG_FMT, condition_code); +d159 1 +a159 1 + vsprintf(tmp_message, +d165 1 +a165 1 + strncpy(new_descriptor->message, tmp_message, CHF_MAX_MESSAGE_LENGTH-1); +@ diff --git a/Chf/RCS/chf_hdlr.c,v b/Chf/RCS/chf_hdlr.c,v new file mode 100644 index 0000000..fc431a3 --- /dev/null +++ b/Chf/RCS/chf_hdlr.c,v @@ -0,0 +1,407 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.12.12.46; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.45.04; author cibrario; state Rel; +branches; +next 1.6; + +1.6 +date 97.01.15.13.44.39; author cibrario; state Exp; +branches; +next 1.1; + +1.1 +date 96.05.28.12.54.28; author cibrario; state Beta; +branches; +next ; + + +desc +@This module implements the CHF functions ChfPushHandler() and +ChfPopHandler() +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_hdlr.c,v 2.1 2000/05/26 14:45:04 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_hdlr.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the CHF functions ChfPushHandler() and + ChfPopHandler() + +.include : Chf.h + +.notes : + $Log: chf_hdlr.c,v $ + Revision 2.1 2000/05/26 14:45:04 cibrario + - Implemented StructuredHelper(), the structured condition handling + helper handler + - Updated ChfPushHandler() to push the structured condition handling + helper when new_handler is CHF_NULL_HANDLER + - unwind_context is now a sigjmp_buf, passed as argument directly, + that is, without additional address operators + - improved documentation of ChfPopHandler() + + Revision 1.6 1997/01/15 13:44:39 cibrario + The function ChfPushHandler() has the new argument 'handler_context'. + + Revision 1.1 1996/05/28 12:54:28 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_hdlr.c,v 2.1 2000/05/26 14:45:04 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* .+ + +.title : StructuredHelper +.kind : C function +.creation : 19-May-2000 +.description : + This function is the structured condition handling helper of CHF. + It's automatically pushed into the condition handler stack by + ChfPushHandler() when its 'new_handler' argument is CHF_NULL_HANDLER, + and performs the following functions: + + - if called during an ordinary signalling operation with a + CHF_FATAL condition, it requests the action CHF_UNWIND_KEEP + + - if called when Chf is in any other state, or with a + severity less than CHF_FATAL, it requests the action CHF_RESIGNAL + + The structured condition handling helper currently makes no use of + handler_context. + +.call : + action = StructuredHelper(desc, state, context); +.input : + const ChfDescriptor *desc, condition descriptor + const ChfState state, current CHF state +.output : + ChfAction action, action requested by the handler +.status_codes : + none +.notes : + 2.1, 19-May-2000, creation + +.- */ +static ChfAction StructuredHelper( + const ChfDescriptor *desc, + const ChfState state, + ChfPointer handler_context +) +{ + ChfAction action; + const ChfDescriptor *d; + + return((state == CHF_SIGNALING && ChfGetSeverity(desc) == CHF_FATAL) + ? CHF_UNWIND_KEEP : CHF_RESIGNAL); +} + + +/* .+ + +.title : ChfPushHandler +.kind : C function +.creation : 13-May-1996 +.description : + This function pushes the new condition handler 'new_handler' with its + associated longjmp context pointed by 'unwind_context' into the handler + stack and returns CHF_S_OK to the caller. If 'new_handler' is + CHF_NULL_HANDLER, the special structured condition handling helper + 'StructuredHelper()' is pushed instead. + + Moreover, this function saves a copy of the pointer 'handler_context'; it + will be passed to 'new_handler' upon each subsequent activation, and + therefore can be used as a private handler context pointer. The user must + assure that the information pointed by 'handler_context', if any, will + remain valid until 'new_handler' is popped from the condition stack. + 'handler_context' may be set to the special (null) value CHF_NULL_POINTER to + indicate that the handler hasn't any private context information. + + If, in the future, the handler will request the CHF_UNWIND action, the + setjmp() function invocation that established 'unwind_context' will appear + to return again. + + 'unwind_context' can be the reserved (null) pointer CHF_NULL_CONTEXT; in + this case, if the handler will request the CHF_UNWIND_ACTION, the + application will be silently terminated calling ChfAbort() with abort code + CHF_ABORT_SILENT. + + If some error occours during the execution, the function will generate + and immediately signal one of the conditions listed below and marked with + (*). The function will never return dorectly to the caller, since all + conditions are CHF_FATAL. + + NOTE: This function calls ChfAbort() with abort code CHF_ABORT_INIT if + the CHF subsystem has not been initialized. + +.call : + ChfPushHandler(new_handler, unwind_context); +.input : + ChfHandler new_handler, new condition handler + void *unwind_context, handler unwind context pointer + ChfPointer handler_context, private handler context pointer +.output : + void +.status_codes : + (*) CHF_F_BAD_STATE, bad CHF state for requested operation + (*) CHF_F_HDLR_STACK_FULL, the handler stack is full +.notes : + 1.1, 13-May-1996, creation + 1.6, 15-Jan-1997, update: + - added the argument 'handler_context' + - improved documentation + 2.1, 19-May-2000, update: + - now using sigjmp_buf as unwind_context + - added StructuredHelper handling + +.- */ +void ChfPushHandler( /* Push a new handler into the stack */ + ChfHandler new_handler, + void *unwind_context, + ChfPointer handler_context +) +{ + /* Make sure that CHF has been correctly initialized and is idle */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if(chf_context.state != CHF_IDLE) + { + ChfCondition CHF_F_BAD_STATE, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + /* Check if the handler stack is full */ + else if(chf_context.handler_sp - chf_context.handler_stack >= + chf_context.handler_stack_size) + { + ChfCondition CHF_F_HDLR_STACK_FULL, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + else + { + chf_context.handler_sp->unwind_context = unwind_context; + chf_context.handler_sp->handler_context = handler_context; + chf_context.handler_sp->handler = + ((new_handler == CHF_NULL_HANDLER) ? StructuredHelper : new_handler); + chf_context.handler_sp++; + } +} + + +/* .+ + +.title : ChfPopHandler +.kind : C function +.creation : 13-May-1996 +.description : + This function pops the topmost condition handler from the handler stack and + returns to the caller. + + If some error occours during the execution, the function will generate + and immediately signal one of the conditions listed below and marked with + (*). The function will never return directly to the caller, since all + conditions are CHF_FATAL. + + NOTE: This function calls ChfAbort() with abort code CHF_ABORT_INIT if + the CHF subsystem has not been initialized. + +.call : + ChfPopHandler(); +.input : + void +.output : + void +.status_codes : + CHF_F_BAD_STATE, bad CHF state for requested operation + CHF_F_HDLR_STACK_FULL, the handler stack is full +.notes : + 1.1, 13-May-1996, creation + 1.6, 15-Jan-1997, update: + - improved documentation + 2.1, 19-May-2000, update: + - improved documentation + +.- */ +void ChfPopHandler( /* Pop a handler */ + void +) +{ + /* Make sure that CHF has been correctly initialized and is idle */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if(chf_context.state != CHF_IDLE) + { + ChfCondition CHF_F_BAD_STATE, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + /* Check if the handler stack is empty */ + else if(chf_context.handler_sp == chf_context.handler_stack) + { + ChfCondition CHF_F_HDLR_STACK_EMPTY, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + /* Discard the topmost condition handler */ + else + --chf_context.handler_sp; +} +@ + + +2.1 +log +@- Implemented StructuredHelper(), the structured condition handling + helper handler +- Updated ChfPushHandler() to push the structured condition handling + helper when new_handler is CHF_NULL_HANDLER +- unwind_context is now a sigjmp_buf, passed as argument directly, + that is, without additional address operators +- improved documentation of ChfPopHandler() +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_hdlr.c,v 1.6 1997/01/15 13:44:39 cibrario Exp cibrario $ +d19 9 +d38 1 +a38 1 +static char rcs_id[] = "$Id: chf_hdlr.c,v 1.6 1997/01/15 13:44:39 cibrario Exp cibrario $"; +d43 1 +d45 1 +d48 5 +@ + + +1.6 +log +@The function ChfPushHandler() has the new argument 'handler_context'. +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_hdlr.c,v 1.1 1996/05/28 12:54:28 cibrario Beta $ +d19 3 +d29 1 +a29 1 +static char rcs_id[] = "$Id: chf_hdlr.c,v 1.1 1996/05/28 12:54:28 cibrario Beta $"; +d43 47 +d96 3 +a98 1 + stack and returns CHF_S_OK to the caller. +d129 1 +a129 1 + jmp_buf *unwind_context, handler unwind context pointer +d141 3 +d148 1 +a148 1 + jmp_buf *unwind_context, +d177 2 +a178 1 + chf_context.handler_sp->handler = new_handler; +d202 1 +a202 1 + ChfPopHandler(new_handler); +d204 1 +a204 1 + const int abort_code, abort_code +d214 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d18 3 +a20 1 + $Log$ +d22 1 +d26 1 +a26 1 +static char rcs_id[] = "$Id$"; +d47 8 +d74 1 +a74 1 + cc = ChfPushHandler(new_handler, unwind_context); +d76 3 +a78 3 + ChfHandler new_handler, + jmp_buf *unwind_context + const int abort_code, abort_code +d80 1 +a80 1 + int cc, condition code +d86 3 +d93 2 +a94 1 + jmp_buf *unwind_context +d121 1 +d150 1 +a150 1 + int cc, condition code +a153 1 + CHF_I_UNWIND_DONE, unwind succesfully completed +d156 2 +@ diff --git a/Chf/RCS/chf_init.c,v b/Chf/RCS/chf_init.c,v new file mode 100644 index 0000000..0e5383a --- /dev/null +++ b/Chf/RCS/chf_init.c,v @@ -0,0 +1,1074 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.14.05.23; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.15.30.42; author cibrario; state Rel; +branches; +next 1.6; + +1.6 +date 97.01.15.13.37.19; author cibrario; state Exp; +branches; +next 1.4; + +1.4 +date 96.09.25.13.29.01; author cibrario; state Beta; +branches; +next 1.1; + +1.1 +date 96.05.28.12.54.58; author cibrario; state Exp; +branches; +next ; + + +desc +@This module implements the CHF initialization function ChfInit() +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_init.c,v 2.1 2000/05/26 15:30:42 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_init.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the CHF initialization function ChfInit() + +.include : Chf.h + +.notes : + $Log: chf_init.c,v $ + Revision 2.1 2000/05/26 15:30:42 cibrario + - Renamed static context chf_context to _chf_context; it is now in + actual use only when mt support is not enabled, otherwise it is + used only as a prototype to prime the per-thread contexts on demand + - Conditional definition of mt support static variables + (context_mutex to access the static master context, fputs_mutex to + ensure the atomicity of DefaultHandler() while it is printing a + sequence of grouped condition messages) and functions (DestroyContext() + to destroy a per-thread context when the owning thread terminates) + - Deeply revised ChfInit() and ChfExit() to implement multithreading + support + - Implemented function _ChfGetContext() to return a pointer to the + per-thread Chf context, creating them on-demand. + + Revision 1.6 1997/01/15 13:37:19 cibrario + The Chf condition handlers now have a third argument: the private handler + context pointer; the code has been updated accordingly. + + Revision 1.4 1996/09/25 13:29:01 cibrario + Added static char[] variable rcs_lib_id; it contains the value of the + CHF_LIBRARY_ID macro. The header Chf.h sets that macro to its RCS Id, + that is also the Id of the whole Chf library. + + Revision 1.1 1996/05/28 12:54:58 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_init.c,v 2.1 2000/05/26 15:30:42 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* Chf Library Id */ +#ifndef lint +static ChfChar rcs_lib_id[] = CHF_LIBRARY_ID; +#endif + +/* CHF context */ +ChfContext _chf_context; + +/* Message separator and severity names for ChfBuildMessage() */ +static const ChfChar separator[] = CHF_MESSAGE_SEPARATOR; +static const ChfChar *severity_name[] = CHF_SEVERITY_NAMES; + + +/* ------------------------------------------------------------------------- + Multithreading support + ------------------------------------------------------------------------- */ +#ifdef _REENTRANT +#include + +/* Mutex to access chf_context during initialization and exit; + mutex to puts condition messages on stderr (DefaultHandler) +*/ +static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t fputs_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Chf data key */ +static pthread_key_t data_key; + +/* This function is called to destroy a Chf context when the owning + thread terminated. +*/ +static void DestroyContext(void *context) +{ + free(((ChfContext *)context)->message_buffer); + free(((ChfContext *)context)->handler_stack); + free(((ChfContext *)context)->condition_stack); + free(context); +} +#endif + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + + +/* .+ + +.title : DefaultHandler +.kind : C function +.creation : 17-May-1996 +.description : + This function is the default condition handler of CHF. It's automatically + pushed into the condition handler stack by ChfInit() and performs the + following functions: + + - if called during an unwind, it returns immediately to the caller, + requesting the action CHF_RESIGNAL, else + + - if the severity of the condition being signalled is greater than + CHF_SUCCESS, it prints the messages associated with the entire + condition group on stderr using the standard function + ChfBuildMessage() to build the messages. + + - if the severity of the condition being signalled is less than + CHF_FATAL, it returns to the caller requesting the action + CHF_CONTINUE, else + + - if the CHF_FATAL condition was NOT signalled during an unwind + operation, it returns to the caller requesting the action + CHF_UNWIND, otherwise it requests the action CHF_RESIGNAL. + + WIN32: + + - stderr stream is not available and modal MessageBox require the + parent's handle to work reliably; the default handler does not print + anything + +.call : + action = DefaultHandler(desc, state, context); +.input : + const ChfDescriptor *desc, condition descriptor + const ChfState state, current CHF state +.output : + ChfAction action, action requested by the handler +.status_codes : + none +.notes : + 1.1, 16-May-1996, creation + 1.6, 15-Jan-1997, update: + - the Chf condition handlers now have a third argument: the private + handler context pointer. + 2.1, 25-May-2000, update: + - added multithreading support + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +static ChfAction DefaultHandler( + const ChfDescriptor *desc, + const ChfState state, + ChfPointer handler_context +) +{ + ChfAction action; + const ChfDescriptor *d; + + if(state == CHF_UNWINDING) + /* If CHF is unwinding, do nothing */ + action = CHF_RESIGNAL; + + else + { + /* Print the condition messages, if necessary. The sequence of fputs() + is done atomically if multithreading support is enabled. + In Win32, the default handler does not print anything. + */ + if(ChfGetSeverity(desc) > CHF_SUCCESS) + { +#ifdef _REENTRANT + if(pthread_mutex_lock(&fputs_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif +#ifndef _WIN32 + for(d = desc; d != CHF_NULL_DESCRIPTOR; d = ChfGetNextDescriptor(d)) + fputs(ChfBuildMessage(d), stderr); +#endif +#ifdef _REENTRANT + if(pthread_mutex_unlock(&fputs_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif + } + + /* Determine the handler action */ + switch(ChfGetSeverity(desc)) + { + case CHF_SUCCESS: + case CHF_INFO: + case CHF_WARNING: + case CHF_ERROR: + { + /* Continue execution if the severity is less than CHF_FATAL */ + action = CHF_CONTINUE; + break; + } + + default: + { + /* The severity of the condition is CHF_FATAL; appempt to unwind if + the fatal condition wasn't signalled during another unwind. + */ + action = ((state == CHF_SIGNAL_UNWINDING) ? CHF_RESIGNAL: CHF_UNWIND); + break; + } + } + } + + /* Return the action code to the Chf handler dispatcher */ + return action; +} + + + +/* .+ + +.title : scopy +.kind : C function +.creation : 16-May-1996 +.description : + This function writes the NUL-terminated string pointed by 'q' starting + from 'p', including the NUL terminator and without trepassing 'p_end'. + The function returns a pointer to the NUL-terminator just written. + +.call : + np = scopy(p, q, p_end); +.input : + char *p, starting position for the write + const char *q, pointer to the string to be copied + char *p_end, pointer to the end of the output area +.output : + char *np, pointer to the NUL-terminator just written +.status_codes : + none +.notes : + 1.1, 16-May-1996, creation + +.- */ +static ChfChar *scopy( + ChfChar *p, + const ChfChar *q, + ChfChar *p_end +) +{ + size_t q_len = ChfStrlen(q); + size_t p_avail = p_end - p; + + if(q_len < p_avail) + { + ChfStrcpy(p, q); + p += q_len; + } + + else if(p_avail > 1) + { + ChfStrncpy(p, q, p_avail-2); + p[p_avail-1] = '\0'; + p = p_end; + } + + return p; +} + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfGetMessage +.kind : C function +.creation : 17-May-1996 +.description : + This function retrieves the message associated with the pair + ('module_id', 'condition_code') and returns a pointer to it. The function + will return 'default_message' if it isn't able to retrieve the message. + If module_id==CHF_ERRNO_SET, the function will use strerror(), if + necessary, to retrieve the requested message. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been correctly initialized. + + NOTE: The returned pointer points to per-thread static storage, which will be + overwritten by subsequent calls to this function. + + WIN32: + + - strerror() is not supported; the last-chance translation of condition + codes in CHF_ERRNO_SET is not performed + +.call : + message = ChfGetMessage(module_id, condition_code, + default_message); +.input : + const int module_id, module identifier + const int condition_code, condition code + const char *default_message, default message +.output : + const char *message, pointer to the retrieved message +.status_codes : + none +.notes : + 1.1, 17-May-1996, creation + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +const ChfChar *ChfGetMessage( /* Retrieve a condition message */ + const int module_id, + const int condition_code, + const ChfChar *default_message +) +{ + const ChfChar *message; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if((message = chf_context.mrs_get(chf_context.mrs_data, + module_id, condition_code, default_message)) == default_message && + module_id == CHF_ERRNO_SET) +# ifdef _WIN32 + message = default_message; +# else + message = strerror(condition_code); +# endif + + return(message); +} + + +/* .+ + +.title : ChfBuildMessage +.kind : C function +.creation : 16-May-1996 +.description : + This function builds the message associated with the given condition + descriptor and returns a pointer to a string containing it. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been correctly initialized. + + NOTE: The returned pointer points to per-thread static storage, which will be + overwritten by subsequent calls to this function. + + WIN32: + + - to save space, the application's name and severity code are not + included in the message + +.call : + msg = ChfBuildMessage(descriptor); +.input : + const ChfDescriptor *descriptor, condition descriptor +.output : + char *msg, pointer to the message associated with 'descriptor' +.status_codes : + none +.notes : + 1.1, 16-May-1996, creation + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +ChfChar *ChfBuildMessage( /* Build a condition message */ + const ChfDescriptor *descriptor +) +{ + ChfChar *tmp_p; + ChfChar *tmp_end; + ChfChar def_message[CHF_DEF_MESSAGE_LENGTH]; + ChfSeverity severity; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + /* Set appropriate pointers to the start/end of the message buffer */ + tmp_p = chf_context.message_buffer; + tmp_end = tmp_p + CHF_MAX_MESSAGE_LENGTH; + +#ifndef _WIN32 + /* The message starts with ": " if the condition is the first of + its condition group, with "\t" if not. + */ + if(descriptor == chf_context.condition_sp-1) + { + tmp_p = scopy(tmp_p, chf_context.app_name, tmp_end); + tmp_p = scopy(tmp_p, separator, tmp_end); + } + + else + tmp_p = scopy(tmp_p, ChfText("\t"), tmp_end); +#endif + + /* The message continues with the module name */ + ChfSprintf(def_message, CHF_DEF_MID_MSG_FMT, ChfGetModuleId(descriptor)); + + tmp_p = scopy(tmp_p, + ChfGetMessage(CHF_MODULE_NAMES_SET, ChfGetModuleId(descriptor), + def_message), tmp_end); + + /* Add also the extended information, if any */ + if(ChfGetLineNumber(descriptor) != CHF_UNKNOWN_LINE_NUMBER) + { + tmp_p = scopy(tmp_p, ChfText(" "), tmp_end); + + ChfSprintf(def_message, CHF_EXTENDED_INFO_FMT, + ChfGetFileName(descriptor), ChfGetLineNumber(descriptor)); + + tmp_p = scopy(tmp_p, def_message, tmp_end); + } + + tmp_p = scopy(tmp_p, separator, tmp_end); + +#ifndef _WIN32 + /* Add the severity code of the message */ + tmp_p = scopy(tmp_p, + ((severity = ChfGetSeverity(descriptor)) < CHF_SUCCESS || + severity > CHF_FATAL) ? CHF_UNKNOWN_SEVERITY : severity_name[severity], + tmp_end); + + tmp_p = scopy(tmp_p, separator, tmp_end); +#endif + + /* The message ends with the partial message from the descriptor */ + tmp_p = scopy(tmp_p, ChfGetPartialMessage(descriptor), tmp_end); + (void)scopy(tmp_p, CHF_MESSAGE_TERMINATOR, tmp_end); + + return chf_context.message_buffer; +} + + +/* .+ + +.title : ChfInit +.kind : C function +.creation : 13-May-1996 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully ChfInit() before using any other CHF + function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_PTHREAD + if a pthread operation fails. + +.call : + cc = ChfInit(app_name, options, + mrs_data, mrs_get, mrs_exit, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const char *app_name, Application's name + const ChfOptions options, Options + void *mrs_data, Message retrieval private data + ChfMrsGet mrs_get, 'GetMessage' function + ChfMrsExit mrs_exit, 'Exit' function + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_MALLOC, FATAL, dynamic memory allocation failed +.notes : + 1.1, 13-May-1996, creation + 1.6, 15-Jan-1997, update: + - updated the call to ChfPushHandler() to accomodate its new interface. + 2.1, 19-May-2000, update: + - added multithreading support + 2.2, 22-Jan-2001, update: + - added Win32 support; a malloc() call was not portable. + +.- */ +int ChfInit( /* Generic initialization */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + void *mrs_data, /* Message retrieval private data */ + ChfMrsGet mrs_get, /* 'GetMessage' function */ + ChfMrsExit mrs_exit, /* 'Exit' function */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ + int cc; + + /* Check that CHF has not been initialized yet */ +#ifndef _REENTRANT + if(_chf_context.state != CHF_UNKNOWN) ChfAbort(CHF_ABORT_DUP_INIT); +#else + /* Reentrant check; lock context_mutex first */ + if(pthread_mutex_lock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + if(_chf_context.state != CHF_UNKNOWN) + { + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + ChfAbort(CHF_ABORT_DUP_INIT); + } +#endif + +#ifndef _REENTRANT + if((_chf_context.condition_stack = + (ChfDescriptor *) + malloc((size_t)(condition_stack_size+1)*sizeof(ChfDescriptor))) == + CHF_NULL_DESCRIPTOR) + cc = CHF_F_MALLOC; + + else if((_chf_context.handler_stack = + (ChfHandlerDescriptor *) + malloc((size_t)handler_stack_size*sizeof(ChfHandlerDescriptor))) == + (ChfHandlerDescriptor *)NULL) + { + free(_chf_context.condition_stack); + cc = CHF_F_MALLOC; + } + + else if((_chf_context.message_buffer = + (ChfChar *)malloc( + (size_t)(CHF_MAX_MESSAGE_LENGTH) * sizeof(ChfChar))) == (ChfChar *)NULL) + { + free(_chf_context.condition_stack); + free(_chf_context.handler_stack); + cc = CHF_F_MALLOC; + } + + else +#else + /* Reentrant init: condition_stack, handler_stack, message_buffer + are not needed in the master Chf context. + Init the Chf data key instead. + */ + _chf_context.condition_stack = CHF_NULL_DESCRIPTOR; + _chf_context.handler_stack = (ChfHandlerDescriptor *)NULL; + _chf_context.message_buffer = (char *)NULL; + + if(pthread_key_create(&data_key, DestroyContext)) + ChfAbort(CHF_ABORT_PTHREAD); +#endif + + { + /* Initialize the CHF context */ + _chf_context.app_name = app_name; + _chf_context.options = options; + _chf_context.mrs_data = mrs_data; + _chf_context.mrs_get = mrs_get; + _chf_context.mrs_exit = mrs_exit; + _chf_context.condition_stack_size = condition_stack_size; + _chf_context.handler_stack_size = handler_stack_size; + _chf_context.exit_code = exit_code; + _chf_context.condition_base = _chf_context.condition_sp = + _chf_context.condition_stack; + _chf_context.handler_sp = _chf_context.handler_stack; + _chf_context.state = CHF_IDLE; + +#ifndef _REENTRANT + /* Push the default handler; in the reentrant case, this will be + done once per thread, when the thread-specific context is primed. + */ + ChfPushHandler(DefaultHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER); +#endif + + cc = CHF_S_OK; + } + +#ifdef _REENTRANT + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif + + return cc; +} + + +/* .+ + +.title : ChfExit +.kind : C function +.creation : 24-May-1996 +.description : + This function shuts down CHF and returns nothing to the caller; after + calling ChfExit() the application can continue, but any subsequent call + to any other CHF function, except the inizialization functions, will abort + the application using ChfAbort() with abort code CHF_ABORT_INIT. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been initialized. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_PTHREAD + if a pthread operation fails. + +.call : + ChfExit(); +.input : + void +.output : + void +.status_codes : + none +.notes : + 1.1, 24-May-1996, creation + 2.1, 19-May-2000, update: + - added multithreading support + +.- */ +void ChfExit( + void +) +{ + /* Check that CHF has been correctly initialized */ +#ifndef _REENTRANT + if(_chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); +#else + /* Reentrant check; lock context_mutex first */ + if(pthread_mutex_lock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + if(_chf_context.state == CHF_UNKNOWN) + { + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + ChfAbort(CHF_ABORT_INIT); + } +#endif + + /* Destroy the context associated with this thread now; this is necessary + to ensure that the context is actually destroyed when a single-threaded + application links with the multithreaded version of Chf: in this case, + pthread_exit() is called *after* ChfExit(), the Chf data key no longer + exists when pthread_exit() is called and the destructor registered + with pthread_key_create() does not take place. + The data pointer associated with the Chf data key is set to NULL to + avoid any subsequent reactivation of the destructor. + */ +#ifdef _REENTRANT + DestroyContext(&chf_context); + if(pthread_setspecific(data_key, (void *)NULL)) + { + (void)pthread_mutex_unlock(&context_mutex); + ChfAbort(CHF_ABORT_PTHREAD); + } +#endif + + /* Shut down the message retrieval subsystem first */ + _chf_context.mrs_exit(_chf_context.mrs_data); + +#ifndef _REENTRANT + /* Free the dynamic memory previously allocated */ + free(_chf_context.message_buffer); + free(_chf_context.handler_stack); + free(_chf_context.condition_stack); +#else + /* Destroy the Chf data key */ + if(pthread_key_delete(data_key)) ChfAbort(CHF_ABORT_PTHREAD); +#endif + + /* Reset CHF state to prevent subsequent calls to ChfExit() itself */ + _chf_context.state = CHF_UNKNOWN; + +#ifdef _REENTRANT + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif +} + + +/* .+ + +.title : _ChfGetContext +.kind : C function +.creation : 19-May-2000 +.description : + This function dynamically primes a new Chf context for the calling + thread (if necessary), and returns a pointer to that context to + the caller. If something does wrong, it aborts the application + with ChfAbort(CHF_ABORT_GET_CONTEXT). + + Results are unpredictable if this function is called before a + successful call to ChfInit(). + +.call : + context = _ChfGetContext(void); +.input : +.output : + ChfContext *context, per-thread Chf context +.status_codes : + none +.notes : + 2.1, 19-May-2000, creation + +.- */ +ChfContext *_ChfGetContext( + void +) +{ + ChfContext *context; + +#ifndef _REENTRANT + /* This function is doomed to fail if _REENTRANT is not defined */ + ChfAbort(CHF_ABORT_GET_CONTEXT); + return((ChfContext *)NULL); +#else + /* Get the thread-specific context pointer associated with the + CHF data key */ + if((context = (ChfContext *)pthread_getspecific(data_key)) + == (ChfContext *)NULL) + { + /* No context pointer; prime a new one, cloning the master context */ + if((context = (ChfContext *)malloc(sizeof(ChfContext))) + == (ChfContext *)NULL) + ChfAbort(CHF_ABORT_GET_CONTEXT); + + memcpy(context, &_chf_context, sizeof(ChfContext)); + + /* Allocate per-thread stacks and message buffer */ + if((context->condition_stack = + (ChfDescriptor *) + malloc((size_t)(context->condition_stack_size+1) + *sizeof(ChfDescriptor))) == CHF_NULL_DESCRIPTOR) + ChfAbort(CHF_ABORT_GET_CONTEXT); + + if((context->handler_stack = + (ChfHandlerDescriptor *) + malloc((size_t)(context->handler_stack_size) + *sizeof(ChfHandlerDescriptor))) == (ChfHandlerDescriptor *)NULL) + { + free(context->condition_stack); + ChfAbort(CHF_ABORT_GET_CONTEXT); + } + + if((context->message_buffer = + (char *) + malloc((size_t)(CHF_MAX_MESSAGE_LENGTH))) == (char *)NULL) + { + free(context->condition_stack); + free(context->handler_stack); + ChfAbort(CHF_ABORT_GET_CONTEXT); + } + + /* Initialize stack pointers */ + context->condition_base = context->condition_sp = + context->condition_stack; + context->handler_sp = context->handler_stack; + + /* Set the thread-specific context pointer; this must be done + before invoking any other function using the context, + including ChfPushHandler() below. + */ + if(pthread_setspecific(data_key, context)) + ChfAbort(CHF_ABORT_GET_CONTEXT); + + /* Push the default handler */ + ChfPushHandler(DefaultHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER); + } + + return context; +#endif +} +@ + + +2.1 +log +@- Renamed static context chf_context to _chf_context; it is now in + actual use only when mt support is not enabled, otherwise it is + used only as a prototype to prime the per-thread contexts on demand +- Conditional definition of mt support static variables + (context_mutex to access the static master context, fputs_mutex to + ensure the atomicity of DefaultHandler() while it is printing a + sequence of grouped condition messages) and functions (DestroyContext() + to destroy a per-thread context when the owning thread terminates) +- Deeply revised ChfInit() and ChfExit() to implement multithreading + support +- Implemented function _ChfGetContext() to return a pointer to the + per-thread Chf context, creating them on-demand. +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_init.c,v 1.6 1997/01/15 13:37:19 cibrario Exp cibrario $ +d18 14 +d48 1 +a48 1 +static char rcs_id[] = "$Id: chf_init.c,v 1.6 1997/01/15 13:37:19 cibrario Exp cibrario $"; +d53 1 +d55 1 +d59 5 +d74 1 +a74 1 +static char rcs_lib_id[] = CHF_LIBRARY_ID; +d81 2 +a82 2 +static const char separator[] = CHF_MESSAGE_SEPARATOR; +static const char *severity_name[] = CHF_SEVERITY_NAMES; +d144 5 +d166 2 +d187 1 +d194 1 +d197 1 +d257 4 +a260 4 +static char *scopy( + char *p, + const char *q, + char *p_end +d263 1 +a263 1 + size_t q_len = strlen(q); +d268 1 +a268 1 + strcpy(p, q); +d274 1 +a274 1 + strncpy(p, q, p_avail-2); +d305 5 +d323 2 +d327 1 +a327 1 +const char *ChfGetMessage( /* Retrieve a condition message */ +d330 1 +a330 1 + const char *default_message +d333 1 +a333 1 + const char *message; +d341 3 +d345 1 +d366 5 +d381 2 +d385 1 +a385 1 +char *ChfBuildMessage( /* Build a condition message */ +d389 3 +a391 3 + char *tmp_p; + char *tmp_end; + char def_message[CHF_DEF_MESSAGE_LENGTH]; +d401 1 +d412 2 +a413 1 + tmp_p = scopy(tmp_p, "\t", tmp_end); +d416 1 +a416 1 + sprintf(def_message, CHF_DEF_MID_MSG_FMT, ChfGetModuleId(descriptor)); +d425 1 +a425 1 + tmp_p = scopy(tmp_p, " ", tmp_end); +d427 1 +a427 1 + sprintf(def_message, CHF_EXTENDED_INFO_FMT, +d435 1 +d443 1 +d496 2 +d501 1 +a501 1 + const char *app_name, /* Application's name */ +d543 2 +a544 1 + (char *)malloc((size_t)(CHF_MAX_MESSAGE_LENGTH))) == (char *)NULL) +@ + + +1.6 +log +@The Chf condition handlers now have a third argument: the private handler +context pointer; the code has been updated accordingly. +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 5 +a21 1 + $Log$ +d34 1 +a34 1 +static char rcs_id[] = "$Id$"; +d57 1 +a57 1 +ChfContext chf_context; +d65 28 +d138 2 +d157 3 +a159 1 + /* Print the condition messages, if necessary */ +d162 3 +d167 3 +d271 1 +a271 1 + NOTE: The returned pointer points to static storage, which will be +d321 1 +a321 1 + NOTE: The returned pointer points to static storage, which will be +d416 3 +d441 2 +d459 11 +a469 1 + if(chf_context.state != CHF_UNKNOWN) ChfAbort(CHF_ABORT_DUP_INIT); +d471 2 +a472 1 + if((chf_context.condition_stack = +d478 1 +a478 1 + else if((chf_context.handler_stack = +d483 1 +a483 1 + free(chf_context.condition_stack); +d487 1 +a487 1 + else if((chf_context.message_buffer = +d490 2 +a491 2 + free(chf_context.condition_stack); + free(chf_context.handler_stack); +d496 13 +d511 17 +a527 14 + chf_context.app_name = app_name; + chf_context.options = options; + chf_context.mrs_data = mrs_data; + chf_context.mrs_get = mrs_get; + chf_context.mrs_exit = mrs_exit; + chf_context.condition_stack_size = condition_stack_size; + chf_context.handler_stack_size = handler_stack_size; + chf_context.exit_code = exit_code; + chf_context.condition_base = chf_context.condition_sp = + chf_context.condition_stack; + chf_context.handler_sp = chf_context.handler_stack; + chf_context.state = CHF_IDLE; + + /* Push the default handler */ +d529 1 +d534 4 +d556 3 +d569 2 +d578 30 +a607 2 + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + +d609 1 +a609 1 + chf_context.mrs_exit(chf_context.mrs_data); +d611 1 +d613 7 +a619 3 + free(chf_context.message_buffer); + free(chf_context.handler_stack); + free(chf_context.condition_stack); +d622 99 +a720 1 + chf_context.state = CHF_UNKNOWN; +@ + + +1.4 +log +@Added static char[] variable rcs_lib_id; it contains the value of the +CHF_LIBRARY_ID macro. The header Chf.h sets that macro to its RCS Id, +that is also the Id of the whole Chf library. +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_init.c,v 1.1 1996/05/28 12:54:58 cibrario Exp cibrario $ +d5 1 +a5 1 +.title : $RCSfile: chf_init.c,v $, condition generation +d17 6 +a22 1 + $Log: chf_init.c,v $ +d30 1 +a30 1 +static char rcs_id[] = "$Id: chf_init.c,v 1.1 1996/05/28 12:54:58 cibrario Exp cibrario $"; +d93 1 +a93 1 + action = DefaultHandler(desc, state); +d103 3 +d110 2 +a111 1 + const ChfState state +d394 2 +d454 1 +a454 1 + ChfPushHandler(DefaultHandler, (jmp_buf *)NULL); +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 3 +a19 1 + $Log$ +d21 1 +d25 1 +a25 1 +static char rcs_id[] = "$Id$"; +d42 5 +@ diff --git a/Chf/RCS/chf_msgc.c,v b/Chf/RCS/chf_msgc.c,v new file mode 100644 index 0000000..2d4cc20 --- /dev/null +++ b/Chf/RCS/chf_msgc.c,v @@ -0,0 +1,292 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:1.3; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.14.06.47; author cibrario; state Exp; +branches; +next 1.3; + +1.3 +date 96.06.21.14.19.22; author cibrario; state Rel; +branches; +next 1.1; + +1.1 +date 96.05.28.12.55.15; author cibrario; state Exp; +branches; +next ; + + +desc +@This module contains the CHF initialization function ChfMsgcatInit() +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_msgc.c,v 1.3 1996/06/21 14:19:22 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_msgc.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 17-May-1996 +.keywords : * +.description : + This module contains the CHF initialization function ChfMsgcatInit() + +.include : Chf.h + +.notes : + $Log: chf_msgc.c,v $ + Revision 1.3 1996/06/21 14:19:22 cibrario + Bug fix: the private context of the message retrieval facility was + never freed by ExitMessage() + + Revision 1.1 1996/05/28 12:55:15 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_msgc.c,v 1.3 1996/06/21 14:19:22 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#ifndef _WIN32 +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- + Private type definitions + ------------------------------------------------------------------------- */ + +#ifndef _WIN32 +typedef struct +{ + nl_catd catalog; /* Message catalog descriptor */ +} + ChfMsgcatContext; +#endif + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + + +#ifndef _WIN32 +static const char *GetMessage( + void *private_context, + const int module_id, + const int condition_code, + const char *default_message +) +{ + return(catgets(((ChfMsgcatContext *)private_context)->catalog, module_id, + condition_code, default_message)); +} + +static void ExitMessage( + void *private_context +) +{ + (void)catclose(((ChfMsgcatContext *)private_context)->catalog); + free(private_context); +} +#endif + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfMsgcatInit +.kind : C function +.creation : 17-May-1996 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully either ChfMsgcatInit() or one of the + other CHF initialization routines before using any other CHF function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + + WIN32: + + - this function is not available due to lack of system support, and + always returns CHF_F_NOT_AVAILABLE + +.call : + cc = ChfMsgcatInit(app_name, options, + msgcat_name, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const char *app_name, Application's name + const ChfOptions options, Options + const char *msgcat_name, Name of the message catalog + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_SETLOCALE, setlocale() failed + CHF_F_CATOPEN, catopen() failed + CHF_F_MALLOC, FATAL, memory allocation failed + CHF_F_NOT_AVAILABLE, FATAL, function not available +.notes : + 1.1, 17-May-1996, creation + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +int ChfMsgcatInit( /* Initialization with msgcat subsystem */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfChar *msgcat_name, /* Name of the message catalog */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ +#ifdef _WIN32 + /* This function always fails in _WIN32, because message catalogs + are not supported. + */ + return CHF_F_NOT_AVAILABLE; + +#else + ChfMsgcatContext *private_context; + int cc; + + if((private_context = + (ChfMsgcatContext *)malloc(sizeof(ChfMsgcatContext))) == + (ChfMsgcatContext *)NULL) + cc = CHF_F_MALLOC; + + else if(setlocale(LC_ALL, "") == (char *)NULL) + { + free(private_context); + cc = CHF_F_SETLOCALE; + } + + else if((private_context->catalog = catopen(msgcat_name, 0)) == + (nl_catd)(-1)) + { + free(private_context); + cc = CHF_F_CATOPEN; + } + + else if((cc = ChfInit(app_name, options, (void *)private_context, + GetMessage, ExitMessage, condition_stack_size, handler_stack_size, + exit_code)) != CHF_S_OK) + { + (void)catclose(private_context->catalog); + free(private_context); + } + + else + cc = CHF_S_OK; + + return cc; + +#endif +} +@ + + +1.3 +log +@Bug fix: the private context of the message retrieval facility was +never freed by ExitMessage() +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_msgc.c,v 1.1 1996/05/28 12:55:15 cibrario Exp cibrario $ +d18 4 +d29 1 +a29 1 +static char rcs_id[] = "$Id: chf_msgc.c,v 1.1 1996/05/28 12:55:15 cibrario Exp cibrario $"; +d34 1 +d36 1 +d39 1 +d42 1 +d44 5 +d61 1 +d67 1 +d75 1 +d94 1 +d117 5 +d140 1 +d143 2 +d148 1 +a148 1 + const char *app_name, /* Application's name */ +d150 1 +a150 1 + const char *msgcat_name, /* Name of the message catalog */ +d156 7 +d196 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 3 +a19 1 + $Log$ +d21 1 +d25 1 +a25 1 +static char rcs_id[] = "$Id$"; +d76 1 +@ diff --git a/Chf/RCS/chf_sig.c,v b/Chf/RCS/chf_sig.c,v new file mode 100644 index 0000000..73d42aa --- /dev/null +++ b/Chf/RCS/chf_sig.c,v @@ -0,0 +1,546 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.14.07.42; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.31.28; author cibrario; state Rel; +branches; +next 1.6; + +1.6 +date 97.01.15.13.34.45; author cibrario; state Exp; +branches; +next 1.1; + +1.1 +date 96.05.28.12.55.51; author cibrario; state Beta; +branches; +next ; + + +desc +@This module implements the condition signalling function of CHF +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_sig.c,v 2.1 2000/05/26 14:31:28 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_sig.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the condition signalling function of CHF + +.include : Chf.h + +.notes : + $Log: chf_sig.c,v $ + Revision 2.1 2000/05/26 14:31:28 cibrario + - Fixed spelling of CHF_SIGNALLING -> CHF_SIGNALING + - Replaced longjmp() with siglongjmp() + - New ChfAction code CHF_UNWIND_KEEP: ChfSignal() unwinds the + execution stack, but keeps the topmost condition group on the + condition stack + + Revision 1.6 1997/01/15 13:34:45 cibrario + Fixed a wrong adjustment of the condition handler stack pointer after + an unwind operation. + Updated the condition handler calls in order to pass to the handlers the + private handler context pointer. + + Revision 1.1 1996/05/28 12:55:51 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_sig.c,v 2.1 2000/05/26 14:31:28 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* .+ + +.title : ChfSignal +.kind : C function +.creation : 10-May-1996 +.description : + This function signals the topmost condition group currently in the + condition stack, and performs the actions requested by the condition + handlers. + + NOTE: This function uses the CHF function 'ChfAbort()' to + abort the application if either + - CHF has not been initialized correctly (abort code CHF_ABORT_INIT) + - the last handler on the handler stack has returned an invalid action + code (abort code CHF_ABORT_INVALID_ACTION) + - one of the handlers has requested the CHF_UNWIND action while + CHF was already unwinding (abort code CHF_ABORT_ALREADY_UNWINDING) + - a CHF_FATAL condition was signalled while CHF was unwinding + (abort code CHF_ABORT_FATAL_UNWINDING) + - all the handlers refused to handle a condition (abort code + CHF_ABORT_IMPROPERLY_HANDLED) + +.call : + ChfSignal(); +.input : + void +.output : + void +.status_codes : + (*) CHF_F_COND_STACK_FULL, the condition stack is full + (*) CHF_F_INVALID_ACTION, invalid handler action (%d) +.notes : + 1.1, 10-May-1996, creation + 1.6, 15-Jan-1997, update & bug fix: + - fixed a wrong adjustment of the condition handler stack pointer after + an unwind operation. + - updated the condition handler calls in order to pass to the handlers the + private handler context pointer, too. + 2.1, 19-May-2000, update: + - added support for structured condition handling + +.- */ +void ChfSignal( + void +) +{ + ChfState saved_state; + ChfDescriptor *saved_condition_base; + ChfDescriptor *current_condition; + ChfHandlerDescriptor *saved_handler_sp; + ChfHandlerDescriptor *handler_up; + ChfHandlerDescriptor *unwind_handler; + ChfAction handler_result; + + /* Check that CHF has been correctly initialized and save the current CHF + state. If CHF was CHF_IDLE change state to CHF_SIGNALING, else if CHF was + CHF_UNWINDING change to CHF_SIGNAL_UNWINDING, otherwise remain in the + previous state (that must be CHF_SIGNALING) + */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + saved_state = chf_context.state; + + if(chf_context.state == CHF_IDLE) + chf_context.state = CHF_SIGNALING; + else if(chf_context.state == CHF_UNWINDING) + chf_context.state = CHF_SIGNAL_UNWINDING; + + if(chf_context.condition_sp > chf_context.condition_base) + { + /* Save the base of the current condition group and then update it in + order to allow further generation of conditions inside the condition + handlers that will be called soon. + */ + current_condition = chf_context.condition_sp-1; + saved_condition_base = chf_context.condition_base; + chf_context.condition_base = chf_context.condition_sp; + + /* Save the current condition handler pointer */ + saved_handler_sp = chf_context.handler_sp; + + /* Call the condition handlers; the loop will exit either: + - when the handler stack is empty, or + - when the current handler returns either CHF_CONTINUE or CHF_UNWIND + */ + handler_result = CHF_RESIGNAL; + while(handler_result == CHF_RESIGNAL && + chf_context.handler_sp > chf_context.handler_stack) + { + chf_context.handler_sp--; + + /* The current condition handler, described by chf_context.handler_sp, + can recursively invoke ChfGenerate() and ChfSignal(). + + ChfGenerate() will store the new condition group starting from + chf_context.condition_sp, that points to the first free slot + of the condition stack. During the first generation, since + chf_context.condition_sp == chf_context.condition_base, the + link pointer of the condition will be NULL and, therefore, + the condition will be the first of a new condition group. + + ChfSignal() will signal the condition group described by the + stack block from chf_context.condition_base to + chf_context.condition_sp-1, if it contains at least one condition; + it will call the handlers starting from chf_context.handler_sp-1, + that describes the handler immediately preceding the current handler. + */ + handler_result = chf_context.handler_sp->handler( + current_condition, chf_context.state, + chf_context.handler_sp->handler_context); + + /* When the CHF state is CHF_SIGNALING, any condition group generated + but not yet signalled when the current handler exits must be merged + with the condition group currently being signalled, in order to allow + the condition handlers to add their own conditions to the condition + group. If the severity of the previous condition group was CHF_FATAL, + the severity of the new group is forced to CHF_FATAL, too. + + When the CHF state is CHF_UNWINDING, the condition group for + which the UNWIND has been requested is 'frozen', no further + modifications are allowed on it, and the condition group is simply + discarded. + */ + if(chf_context.condition_sp > chf_context.condition_base) + { + if(chf_context.state == CHF_SIGNALING) + { + /* Force the new severity to CHF_FATAL if necessary */ + if(ChfGetSeverity(current_condition) == CHF_FATAL) + ChfGetSeverity(chf_context.condition_sp-1) = CHF_FATAL; + + /* Link together the condition groups */ + chf_context.condition_base->next = current_condition; + current_condition = chf_context.condition_sp-1; + chf_context.condition_base = chf_context.condition_sp; + } + + else + chf_context.condition_sp = chf_context.condition_base; + } + + /* The action CHF_CONTINUE is not allowed if the current condition + severity is CHF_FATAL; it's automatically changed to CHF_RESIGNAL + */ + if(handler_result == CHF_CONTINUE && + ChfGetSeverity(current_condition) == CHF_FATAL) + handler_result = CHF_RESIGNAL; + } + + /* Perform the action requested by the last condition handler invoked */ + switch(handler_result) + { + case CHF_CONTINUE: + { + /* Restore the handler stack pointer; the next ChfSignal() invoked + from our same nesting level will invoke our same handler chain + again. + */ + chf_context.handler_sp = saved_handler_sp; + + /* Discard the current condition group */ + chf_context.condition_base = chf_context.condition_sp = + saved_condition_base; + + /* Restore che saved CHF state */ + chf_context.state = saved_state; + + /* Continue from the instruction following the ChfSignal() */ + break; + } + + case CHF_UNWIND: + case CHF_UNWIND_KEEP: + { + /* Unwind the execution stack. Check that another unwind isn't + already in progress + */ + if(chf_context.state == CHF_UNWINDING) + ChfAbort(CHF_ABORT_ALREADY_UNWINDING); + + else + { + /* Change CHF state */ + chf_context.state = CHF_UNWINDING; + + /* chf_context.handler_sp points to the condition handler that + has requested the unwind; call all the handlers again, starting + from saved_handler_sp (top of the handler stack) up to and + including chf_context.handler_sp. + */ + handler_up = saved_handler_sp; + + while(handler_up > chf_context.handler_sp) + { + ChfAction unw_handler_result; + handler_up--; + + /* The current condition handler, described by handler_up + can recursively invoke ChfGenerate() and ChfSignal(). + + ChfGenerate() will store the new condition group starting from + chf_context.condition_sp, that points to the first free slot + of the condition stack. During the first generation, since + chf_context.condition_sp == chf_context.condition_base, the + link pointer of the condition will be NULL and, therefore, + the condition will be the first of a new condition group. + + ChfSignal() will generate the condition group described by the + stack block from chf_context.condition_base to + chf_context.condition_sp-1, if it contains at least one + condition; it will call the handlers starting from + chf_context.handler_sp-1, that describes the handler + immediately preceding the handler that has requested the unwind. + + Further unwind requests are not allowed, and will trigger + the condition CHF_F_UNWINDING + */ + unw_handler_result = handler_up->handler( + current_condition, chf_context.state, + handler_up->handler_context); + + /* When the CHF state is CHF_UNWINDING, any condition group + generated but not yet signalled when the current handler + returns must be discarded + */ + chf_context.condition_sp = chf_context.condition_base; + } + + /* Restore the handler stack pointer, discarding the unwinded + condition handlers. chf_context.handler_sp points to the + handler that requested the unwind; that handler has been + unwinded and its location is now the first free slot in the + condition handler stack. + */ + unwind_handler = chf_context.handler_sp; + + if(handler_result == CHF_UNWIND) + { + /* Normal unwind: + restore the condition stack pointers, discarding all condition + groups. + */ + chf_context.condition_base = chf_context.condition_sp = + chf_context.condition_stack; + } + else + { + /* Special unwind for structured condition handling: + restore the condition_base pointer only, to keep the + topmost condition group on the condition stack. This way, + the condition group remains accessible after the unwind. + */ + chf_context.condition_base = saved_condition_base; + } + + /* Change the CHF state to CHF_IDLE, and execute longjmp(). + If the handler hasn't a valid unwind_context associated with it, + simply abort the application. + */ + chf_context.state = CHF_IDLE; + + if(unwind_handler->unwind_context == CHF_NULL_CONTEXT) + ChfAbort(CHF_ABORT_SILENT); + else + ChfSiglongjmp(unwind_handler->unwind_context, 1); + } + + break; + } + + case CHF_RESIGNAL: + { + ChfAbort( + (chf_context.state == CHF_SIGNALING) ? + CHF_ABORT_IMPROPERLY_HANDLED : CHF_ABORT_FATAL_UNWINDING); + + break; + } + + default: + { + /* Invalid handler action detected; generate and immediately signal a + condition if the broken handler isn't the last handler on the stack, + otherwise call ChfAbort() + */ + if(chf_context.handler_sp > chf_context.handler_stack) + { + ChfCondition CHF_F_INVALID_ACTION, CHF_FATAL, handler_result + ChfEnd; + + ChfSignal(); + } + + else + ChfAbort(CHF_ABORT_INVALID_ACTION); + + break; + } + } + } + + /* Restore the old CHF state */ + chf_context.state = saved_state; +} + + +/* .+ + +.title : ChfDiscard +.kind : C function +.creation : 17-May-1996 +.description : + This function discards the topmost condition group currently in the + condition stack, without signalling it. The function does nothing if + the condition stack is empty. + + NOTE: This function uses the CHF function 'ChfAbort()' to + abort the application if either + - CHF has not been initialized correctly (abort code CHF_ABORT_INIT) + +.call : + ChfDiscard(); +.input : + void +.output : + void +.status_codes : + none +.notes : + 1.1, 17-May-1996, creation + +.- */ +void ChfDiscard( /* Discard the current conditions */ + void +) +{ + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + /* Reset the current condition stack pointer to the current condition + stack base pointer + */ + chf_context.condition_sp = chf_context.condition_base; +} +@ + + +2.1 +log +@- Fixed spelling of CHF_SIGNALLING -> CHF_SIGNALING +- Replaced longjmp() with siglongjmp() +- New ChfAction code CHF_UNWIND_KEEP: ChfSignal() unwinds the + execution stack, but keeps the topmost condition group on the + condition stack +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_sig.c,v 1.6 1997/01/15 13:34:45 cibrario Exp cibrario $ +d18 7 +d38 1 +a38 1 +static char rcs_id[] = "$Id: chf_sig.c,v 1.6 1997/01/15 13:34:45 cibrario Exp cibrario $"; +d43 1 +d45 1 +d48 5 +d320 1 +a320 1 + siglongjmp(unwind_handler->unwind_context, 1); +@ + + +1.6 +log +@Fixed a wrong adjustment of the condition handler stack pointer after +an unwind operation. +Updated the condition handler calls in order to pass to the handlers the +private handler context pointer. +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 7 +a23 1 + $Log$ +d31 1 +a31 1 +static char rcs_id[] = "$Id$"; +d81 2 +d98 1 +a98 1 + state. If CHF was CHF_IDLE change state to CHF_SIGNALLING, else if CHF was +d100 1 +a100 1 + previous state (that must be CHF_SIGNALLING) +d106 1 +a106 1 + chf_context.state = CHF_SIGNALLING; +d153 1 +a153 1 + /* When the CHF state is CHF_SIGNALLING, any condition group generated +d167 1 +a167 1 + if(chf_context.state == CHF_SIGNALLING) +d214 1 +d236 1 +d259 1 +a259 1 + handler_result = handler_up->handler( +d278 18 +a295 5 + /* Restore the condition stack pointers, discarding all condition + groups. + */ + chf_context.condition_base = chf_context.condition_sp = + chf_context.condition_stack; +d306 1 +a306 1 + longjmp(*(unwind_handler->unwind_context), 1); +d315 1 +a315 1 + (chf_context.state == CHF_SIGNALLING) ? +@ + + +1.1 +log +@Initial revision +@ +text +@d18 2 +d21 1 +d70 5 +d142 2 +a143 1 + current_condition, chf_context.state); +d250 2 +a251 1 + current_condition, chf_context.state); +d262 3 +a264 2 + handler that requested the unwind and must be adjusted to + point to the handler that immediately precedes it. +d266 1 +a266 1 + unwind_handler = chf_context.handler_sp--; +@ diff --git a/Chf/RCS/chf_st.c,v b/Chf/RCS/chf_st.c,v new file mode 100644 index 0000000..4eac73a --- /dev/null +++ b/Chf/RCS/chf_st.c,v @@ -0,0 +1,295 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:1.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.14.08.45; author cibrario; state Exp; +branches; +next 1.1; + +1.1 +date 96.05.28.12.56.14; author cibrario; state Rel; +branches; +next ; + + +desc +@This module implements the CHF initialization function ChfStaticInit() +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_st.c,v 1.1 1996/05/28 12:56:14 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_st.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 24-May-1996 +.keywords : * +.description : + This module implements the CHF initialization function ChfStaticInit() + +.include : Chf.h + +.notes : + $Log: chf_st.c,v $ + Revision 1.1 1996/05/28 12:56:14 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_st.c,v 1.1 1996/05/28 12:56:14 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- + Private type definitions + ------------------------------------------------------------------------- */ + +typedef struct +{ + const ChfTable *table; + size_t size; +} + ChfStaticContext; + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +/* Win32 does not have bsearch(); + provide a simple one from glibc here. +*/ +static void *bsearch( + const void *key, + const void *base, + size_t nmemb, + size_t size, + int (*compar)(const void *, const void *) +) +{ + size_t l, u, idx; + const void *p; + int comparison; + + l = 0; + u = nmemb; + while (l < u) + { + idx = (l + u) / 2; + p = (void *) (((const char *) base) + (idx * size)); + comparison = (*compar) (key, p); + if (comparison < 0) + u = idx; + else if (comparison > 0) + l = idx + 1; + else + return (void *) p; + } + + return NULL; +} +#endif + +#define GT 1 +#define LT -1 +#define EQ 0 + +static int Search( + const void *l, + const void *r +) +{ + if(((ChfTable *)l)->module > ((ChfTable *)r)->module) + return(GT); + + else if(((ChfTable *)l)->module < ((ChfTable *)r)->module) + return(LT); + + else if(((ChfTable *)l)->code > ((ChfTable *)r)->code) + return(GT); + + else if(((ChfTable *)l)->code < ((ChfTable *)r)->code) + return(LT); + + return(EQ); +} + +static const ChfChar *StGetMessage( + void *private_context, + const int module_id, + const int condition_code, + const ChfChar *default_message +) +{ + ChfTable key; + ChfTable *res; + + key.module = module_id; + key.code = condition_code; + + if((res = bsearch(&key, ((ChfStaticContext *)private_context)->table, + ((ChfStaticContext *)private_context)->size, sizeof(ChfTable), Search)) == + (void *)NULL) + return(default_message); + + return(((ChfTable *)res)->msg_template); +} + +static void ExitMessage( + void *private_context +) +{ +} + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfStaticInit +.kind : C function +.creation : 24-May-1996 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully either ChfStaticInit() or one of the + other CHF initialization routines before using any other CHF function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + +.call : + cc = ChfStaticInit(app_name, options, + table, table_size, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const char *app_name, Application's name + const ChfOptions options, Options + const ChfTable *table, pointer to the static message table + const size_t table_size, size of the table (# of entries) + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_MALLOC, FATAL, memory allocation failed +.notes : + 1.1, 27-May-1996, creation + +.- */ +int ChfStaticInit( /* Initialization with static message tables */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfTable *table, /* Static message table */ + const size_t table_size, /* Size of the message table */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ + ChfStaticContext *private_context; + int cc; + + if((private_context = + (ChfStaticContext *)malloc(sizeof(ChfStaticContext))) == + (ChfStaticContext *)NULL) + cc = CHF_F_MALLOC; + + else if((cc = ChfInit(app_name, options, (void *)private_context, + StGetMessage, ExitMessage, condition_stack_size, handler_stack_size, + exit_code)) != CHF_S_OK) + free(private_context); + + else + { + private_context->table = table; + private_context->size = table_size; + cc = CHF_S_OK; + } + + return cc; +} +@ + + +1.1 +log +@Initial revision +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$, condition generation +d17 3 +a19 1 + $Log$ +d21 1 +d25 1 +a25 1 +static char rcs_id[] = "$Id$"; +d30 1 +d32 1 +d36 5 +d65 35 +d124 1 +a124 1 +static const char *GetMessage( +d128 1 +a128 1 + const char *default_message +d142 1 +a142 1 + return(((ChfTable *)res)->template); +d194 1 +a194 1 + const char *app_name, /* Application's name */ +d212 1 +a212 1 + GetMessage, ExitMessage, condition_stack_size, handler_stack_size, +@ diff --git a/Chf/RCS/chf_top.c,v b/Chf/RCS/chf_top.c,v new file mode 100644 index 0000000..261c1dc --- /dev/null +++ b/Chf/RCS/chf_top.c,v @@ -0,0 +1,192 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2 + Rel_2_1:2.1; +locks; strict; +comment @ @; + + +2.2 +date 2001.01.25.14.09.21; author cibrario; state Exp; +branches; +next 2.1; + +2.1 +date 2000.05.26.14.23.33; author cibrario; state Rel; +branches; +next 1.2; + +1.2 +date 96.06.11.12.47.17; author cibrario; state Beta; +branches; +next ; + + +desc +@This module implements the CHF function ChfGetTopCondition() +@ + + +2.2 +log +@Added partial Win32 support (Windows CE only). +@ +text +@/* .+ + +.identifier : $Id: chf_top.c,v 2.1 2000/05/26 14:23:33 cibrario Rel cibrario $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_top.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 5-Jun-1996 +.keywords : * +.description : + This module implements the CHF function ChfGetTopCondition() + +.include : Chf.h + +.notes : + $Log: chf_top.c,v $ + Revision 2.1 2000/05/26 14:23:33 cibrario + ChfGetTopCondition() used to return a pointer to the wrong condition + descriptor; fixed. + + Revision 1.2 1996/06/11 12:47:17 cibrario + file creation + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_top.c,v 2.1 2000/05/26 14:23:33 cibrario Rel cibrario $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfGetTopCondition +.kind : C function +.creation : 5-Jun-1996 +.description : + This function returns to the caller a pointer to the top condition of + the current condition group. It generates and immediately signals the + condition CHF_F_BAD_STATE if the current condition group is empty. + + + NOTE: During condition signalling, CHF creates a new, empty, condition group + immediately before starting the invocation sequence of the condition + handlers, as described in the documentation. Therefore + ChfGetTopCondition(), if called from a condition handler, will return + a pointer to the top condition generated during the handling ONLY, and + NOT to the top condition of the condition group being signalled. The + latter pointer is directly available, as an argument, to the condition + handlers. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been correctly initialized. + + NOTE: The returned pointer is no longer valid when any other CHF function + is called after ChfGetTopCondition(). + +.call : + d = ChfGetTopCondition(); +.input : + void +.output : + const ChfDescriptor *d, condition descriptor +.status_codes : + +.notes : + 1.2, 17-May-1996, creation + 2.1, 24-May-2000, bug fix: + - condition stack referenced incorrectly + +.- */ +const ChfDescriptor *ChfGetTopCondition( /* Retrieve top condition */ + void +) +{ + ChfDescriptor *d; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if((d = chf_context.condition_sp) == chf_context.condition_base) + { + ChfCondition CHF_F_BAD_STATE, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* The top element of the condition group is the element immediately + below the stack pointer. + */ + return d-1; +} +@ + + +2.1 +log +@ChfGetTopCondition() used to return a pointer to the wrong condition +descriptor; fixed. +@ +text +@d3 1 +a3 1 +.identifier : $Id: chf_top.c,v 1.2 1996/06/11 12:47:17 cibrario Beta cibrario $ +d18 4 +d29 1 +a29 1 +static char rcs_id[] = "$Id: chf_top.c,v 1.2 1996/06/11 12:47:17 cibrario Beta cibrario $"; +d34 1 +d36 1 +d40 5 +@ + + +1.2 +log +@file creation +@ +text +@d3 1 +a3 1 +.identifier : $Id$ +d5 1 +a5 1 +.title : $RCSfile$ +d17 3 +a19 1 + $Log$ +d21 1 +d25 1 +a25 1 +static char rcs_id[] = "$Id$"; +d78 2 +d97 4 +a100 1 + return d; +@ diff --git a/Chf/RCS/chf_win32.c,v b/Chf/RCS/chf_win32.c,v new file mode 100644 index 0000000..8fc975d --- /dev/null +++ b/Chf/RCS/chf_win32.c,v @@ -0,0 +1,212 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2; +locks; strict; +comment @ * @; + + +2.2 +date 2001.01.25.14.11.58; author cibrario; state Exp; +branches; +next ; + + +desc +@@ + + +2.2 +log +@*** empty log message *** +@ +text +@/* .+ + +.identifier : $Id$ +.context : CHF, Condition Handling Facility +.title : $RCSfile$, Win32 initialization function +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 19-Jan-2001 +.keywords : * +.description : + This module contains the CHF initialization function ChfWin32Init() + +.include : Chf.h + +.notes : + $Log$ + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id$"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- + Private type definitions + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +typedef struct +{ + HINSTANCE instance; /* App. instance handle */ + ChfChar buffer[CHF_MAX_MESSAGE_LENGTH]; /* Temporary buffer */ +} + ChfWin32Context; +#endif + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +static const ChfChar *Win32GetMessage( + void *private_context, + const int module_id, + const int condition_code, + const ChfChar *default_message +) +{ + if(!LoadString( + ((ChfWin32Context *)private_context)->instance, + module_id*1000 + condition_code, + ((ChfWin32Context *)private_context)->buffer, + CHF_MAX_MESSAGE_LENGTH-1)) + return default_message; + + return ((ChfWin32Context *)private_context)->buffer; +} + +static void ExitMessage( + void *private_context +) +{ + free(private_context); +} +#endif + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfWin32Init +.kind : C function +.creation : 19-Jan-2001 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully either ChfWin32Init() or one of the + other CHF initialization routines before using any other CHF function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + + WIN32: + + - This function is available in Win32 only; it will return + CHF_F_NOT_AVAILABLE on Unix platforma. + + - message retrieval is done through the LoadString() Win32 function. + This function does not support message sets, so the linear message id + passed to it is made by module_id*1000 + condition_code. The following + limits are in effect: + 0 <= condition_code <= 999 + 0 <= module_id <= 64 + +.call : + cc = ChfWin32Init(app_name, options, + msgcat_name, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const ChfChar *app_name, Application's name + const ChfOptions options, Options + HINSTANCE instance, App. instance handle + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_MALLOC, FATAL, memory allocation failed + CHF_F_NOT_AVAILABLE, FATAL, function not available +.notes : + 2.2, 19-Jan-2001, creation + +.- */ +int ChfWin32Init( /* Initialization within _WIN32 */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ +#ifndef _WIN32 + void *instance, /* Fake arguments */ +#else + HINSTANCE instance, /* App. instance handle */ +#endif + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ +#ifndef _WIN32 + /* This function is available only in Win32 */ + return CHF_F_NOT_AVAILABLE; + +#else + ChfWin32Context *private_context; + int cc; + + if((private_context = + (ChfWin32Context *)malloc(sizeof(ChfWin32Context))) == + (ChfWin32Context *)NULL) + cc = CHF_F_MALLOC; + + else if((cc = ChfInit(app_name, options, (void *)private_context, + Win32GetMessage, ExitMessage, condition_stack_size, handler_stack_size, + exit_code)) != CHF_S_OK) + { + free(private_context); + } + + else + { + /* Save Win32 specific context items into private Chf context */ + private_context->instance = instance; + + cc = CHF_S_OK; + } + + return cc; + +#endif +} +@ diff --git a/Chf/RCS/libChf.vcp,v b/Chf/RCS/libChf.vcp,v new file mode 100755 index 0000000..976f52b --- /dev/null +++ b/Chf/RCS/libChf.vcp,v @@ -0,0 +1,509 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2; +locks; strict; +comment @# @; + + +2.2 +date 2001.01.25.14.12.47; author cibrario; state Exp; +branches; +next ; + + +desc +@@ + + +2.2 +log +@*** empty log message *** +@ +text +@# Microsoft eMbedded Visual Tools Project File - Name="libChf" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE x86em) Static Library" 0x7f04 +# TARGTYPE "Win32 (WCE ARM) Static Library" 0x8504 + +CFG=libChf - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libChf.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libChf.vcn" CFG="libChf - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libChf - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Static Library") +!MESSAGE "libChf - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Static Library") +!MESSAGE "libChf - Win32 (WCE x86em) Release" (based on "Win32 (WCE x86em) Static Library") +!MESSAGE "libChf - Win32 (WCE x86em) Debug" (based on "Win32 (WCE x86em) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "ARMRel" +# PROP Intermediate_Dir "ARMRel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=clarm.exe +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /Oxs /M$(CECrtMT) /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "ARMDbg" +# PROP Intermediate_Dir "ARMDbg" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=clarm.exe +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "X86EMRel" +# PROP BASE Intermediate_Dir "X86EMRel" +# PROP BASE CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "X86EMRel" +# PROP Intermediate_Dir "X86EMRel" +# PROP CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_LIB" /YX /Oxs /Gz /c +# ADD CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_LIB" /YX /Oxs /Gz /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "X86EMDbg" +# PROP BASE Intermediate_Dir "X86EMDbg" +# PROP BASE CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "X86EMDbg" +# PROP Intermediate_Dir "X86EMDbg" +# PROP CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_LIB" /YX /Gz /c +# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_LIB" /FR /YX /Gz /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ENDIF + +# Begin Target + +# Name "libChf - Win32 (WCE ARM) Release" +# Name "libChf - Win32 (WCE ARM) Debug" +# Name "libChf - Win32 (WCE x86em) Release" +# Name "libChf - Win32 (WCE x86em) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\chf_abrt.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_gen.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_hdlr.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_init.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_msgc.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_sig.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_st.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_top.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_win32.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Chf.h +# End Source File +# Begin Source File + +SOURCE=.\ChfPriv.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "rc" +# Begin Source File + +SOURCE=.\chf.rc +# End Source File +# End Group +# End Target +# End Project +@ diff --git a/Chf/RCS/resource.h,v b/Chf/RCS/resource.h,v new file mode 100755 index 0000000..167b8c0 --- /dev/null +++ b/Chf/RCS/resource.h,v @@ -0,0 +1,39 @@ +head 2.2; +access; +symbols + V4_1_1_1:2.2; +locks; strict; +comment @ * @; + + +2.2 +date 2001.01.25.14.13.01; author cibrario; state Exp; +branches; +next ; + + +desc +@@ + + +2.2 +log +@*** empty log message *** +@ +text +@//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by chf.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif +@ diff --git a/Chf/RCS/test01.c,v b/Chf/RCS/test01.c,v new file mode 100644 index 0000000..8635c41 --- /dev/null +++ b/Chf/RCS/test01.c,v @@ -0,0 +1,118 @@ +head 2.1; +access; +symbols + V4_1_1_1:2.1 + Rel_2_1:2.1; +locks; strict; +comment @ * @; + + +2.1 +date 2000.05.29.13.55.50; author cibrario; state Rel; +branches; +next ; + + +desc +@@ + + +2.1 +log +@*** empty log message *** +@ +text +@/* $Id$ + Chf test program. + Simple initialization. + + +*/ + +#include +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + + +int main(int argc, char *argv[]) +{ + int st; + const char *msg; + const ChfDescriptor *d, *e; + + puts("test01"); + + system("gencat test01.cat test01.msf"); + system("gencat test01.cat chf.msf"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + + /* ChfGetMessage: + message (CHF_MODULE_ID, 1) exists, (CHF_MODULE_ID, 2) does not + */ + msg = ChfGetMessage(CHF_MODULE_ID, 1, "Default_1"); + if(strcmp(msg, "Set_255,Message_1")) exit(EXIT_FAILURE); + msg = ChfGetMessage(CHF_MODULE_ID, 2, "Default_2"); + if(strcmp(msg, "Default_2")) exit(EXIT_FAILURE); + + /* Generate a condition and check descriptor; this is line 46 */ + ChfCondition 3, CHF_WARNING, 456 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 3 + || d->severity != CHF_WARNING + || d->line_number != 46 + || strcmp(d->file_name, "test01.c") + || strcmp(d->message, "Set_255,Arg_456,Message_3") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Generate another condition and check; this is line 60 */ + ChfCondition 4, CHF_INFO, "arg" ChfEnd; + + if((e = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + e->module_id != CHF_MODULE_ID + || e->condition_code != 4 + || e->severity != CHF_INFO + || e->line_number != 60 + || strcmp(e->file_name, "test01.c") + || strcmp(e->message, "Set_255,Arg_arg,Message_4") + || e->next != d + ) exit(EXIT_FAILURE); + + /* Discard the previous condition group and create a new one */ + ChfDiscard(); + + /* This is line 77 */ + ChfCondition 5, CHF_ERROR, 456, 789 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 5 + || d->severity != CHF_ERROR + || d->line_number != 77 + || strcmp(d->file_name, "test01.c") + || strcmp(d->message, "Set_255,Arg_456-789,Message_5") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Exit Chf */ + ChfExit(); + exit(EXIT_SUCCESS); +} +@ diff --git a/Chf/RCS/test01.msf,v b/Chf/RCS/test01.msf,v new file mode 100644 index 0000000..216ba8d --- /dev/null +++ b/Chf/RCS/test01.msf,v @@ -0,0 +1,43 @@ +head 2.1; +access; +symbols + V4_1_1_1:2.1 + Rel_2_1:2.1; +locks; strict; +comment @# @; + + +2.1 +date 2000.05.29.13.09.38; author cibrario; state Rel; +branches; +next ; + + +desc +@@ + + +2.1 +log +@*** empty log message *** +@ +text +@$set 1 +255 Chf Test + +$set 255 +1 Set_255,Message_1 +3 Set_255,Arg_%d,Message_3 +4 Set_255,Arg_%s,Message_4 +5 Set_255,Arg_%d-%d,Message_5 +6 Set_255,Message_6 (thread %d, sub-condition) +7 Set_255,Message_7 (thread %d, main) +8 Set_255,Message_8 (thread %d, main) +9 Set_255,Message_9 + +10 Invalid descriptor link detected +11 Bad hdlr stack push count %d; should be %d +12 Bad cond stack push count %d; should be %d + +20 Structured exc handling test cond +@ diff --git a/Chf/RCS/test02.c,v b/Chf/RCS/test02.c,v new file mode 100644 index 0000000..9e90e36 --- /dev/null +++ b/Chf/RCS/test02.c,v @@ -0,0 +1,146 @@ +head 2.1; +access; +symbols + V4_1_1_1:2.1 + Rel_2_1:2.1; +locks; strict; +comment @ * @; + + +2.1 +date 2000.05.29.13.56.44; author cibrario; state Rel; +branches; +next ; + + +desc +@@ + + +2.1 +log +@*** empty log message *** +@ +text +@/* $Id: test02.c,v 2.1 2000/05/29 13:09:53 cibrario Exp cibrario $ + Chf test program. + Simple initialization - multithreaded. + + +*/ + +#include +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + + +void *task(void *arg) +{ + const char *msg; + const ChfDescriptor *d, *e; + + printf("\tThread %d\n", (int)arg); + + /* message (CHF_MODULE_ID, 1) exists, (CHF_MODULE_ID, 2) does not */ + msg = ChfGetMessage(CHF_MODULE_ID, 1, "Default_1"); + if(strcmp(msg, "Set_255,Message_1")) exit(EXIT_FAILURE); + msg = ChfGetMessage(CHF_MODULE_ID, 2, "Default_2"); + if(strcmp(msg, "Default_2")) exit(EXIT_FAILURE); + + /* Generate a condition and check descriptor; this is line 36 */ + ChfCondition 3, CHF_WARNING, 456 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 3 + || d->severity != CHF_WARNING + || d->line_number != 36 + || strcmp(d->file_name, "test02.c") + || strcmp(d->message, "Set_255,Arg_456,Message_3") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Generate another condition and check; this is line 50 */ + ChfCondition 4, CHF_INFO, "arg" ChfEnd; + + if((e = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + e->module_id != CHF_MODULE_ID + || e->condition_code != 4 + || e->severity != CHF_INFO + || e->line_number != 50 + || strcmp(e->file_name, "test02.c") + || strcmp(e->message, "Set_255,Arg_arg,Message_4") + || e->next != d + ) exit(EXIT_FAILURE); + + /* Discard the previous condition group and create a new one */ + ChfDiscard(); + + /* This is line 67 */ + ChfCondition 5, CHF_ERROR, 456, 789 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 5 + || d->severity != CHF_ERROR + || d->line_number != 67 + || strcmp(d->file_name, "test02.c") + || strcmp(d->message, "Set_255,Arg_456-789,Message_5") + || d->next != NULL + ) exit(EXIT_FAILURE); + + return (void *)0; +} + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test02"); + +#ifdef _REENTRANT + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + + +void *task(void *arg) +{ + const char *msg; + const ChfDescriptor *d, *e; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + printf("\tThread %d\n", (int)arg); + + /* Generate a condition group and signal it */ + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + ChfSignal(); + + return (void *)0; +} + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; + +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test03"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + +#ifdef _REENTRANT + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +struct tdata_s +{ + const ChfDescriptor *d, *e; + int phase; +}; + +ChfAction h1( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + if(c != tdata_p->e || + ChfGetNextDescriptor(c) != tdata_p->d) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + + else + action = CHF_CONTINUE; + + return action; + +} + +ChfAction h2( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + switch(s) + { + case CHF_SIGNALING: + { + if(c != tdata_p->e + || ChfGetNextDescriptor(c) != tdata_p->d + || (tdata_p->phase != 2 && tdata_p->phase != 4)) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + + else + { + action = (ChfGetConditionCode(c) != 8 ? CHF_CONTINUE : CHF_UNWIND); + } + break; + } + case CHF_UNWINDING: + { + if(tdata_p->phase != 4) exit(EXIT_FAILURE); + tdata_p->phase = 5; + action = CHF_CONTINUE; + break; + } + default: + { + exit(EXIT_FAILURE); + } + } + return action; +} + +ChfAction h3( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + /* This handler must be invoked only during the first signal */ + if(tdata_p->phase != 3) exit(EXIT_FAILURE); + + switch(s) + { + case CHF_SIGNALING: + { + if(ChfGetConditionCode(c) != 9 || + ChfGetNextDescriptor(c) != NULL) + { + exit(EXIT_FAILURE); + } + + else + { + tdata_p->phase = 4; + action = CHF_CONTINUE; + } + break; + } + default: + { + exit(EXIT_FAILURE); + } + } + return action; +} + +ChfAction h4( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + /* This handler must be invoked only during the first signal */ + if(tdata_p->phase != 2) exit(EXIT_FAILURE); + + switch(s) + { + case CHF_SIGNALING: + { + if(c != tdata_p->e + || ChfGetNextDescriptor(c) != tdata_p->d) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + + else + { + /* This generates a new group and signals it */ + tdata_p->phase = 3; + ChfCondition 9, CHF_INFO ChfEnd; + ChfSignal(); + + if(tdata_p->phase != 4) exit(EXIT_FAILURE); + tdata_p->phase = 5; + + if(c != tdata_p->e + || ChfGetNextDescriptor(c) != tdata_p->d) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + else + action = CHF_CONTINUE; + } + break; + } + default: + { + exit(EXIT_FAILURE); + } + } + return action; +} + +void *task(void *arg) +{ + volatile struct tdata_s tdata; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + printf("\tThread %d\n", (int)arg); + + /* Push the handler */ + ChfPushHandler(h1, NULL, (ChfPointer)(&tdata)); + + /* Generate a condition group and signal it */ + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + /* The sleep() is here to increase contention between threads */ + sleep(1); + ChfSignal(); + + /* Pop the handler */ + ChfPopHandler(); + + /* Generate a new condition group with (apparently) wrong linkage + and signal it; this checks that the handler has actually been + removed. + */ + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = NULL; + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = NULL; + ChfSignal(); + + /* Conditional unwind test */ + { + sigjmp_buf jb; + + tdata.phase = 0; + if(setjmp(jb) == 0) + { + ChfPushHandler(h2, jb, (ChfPointer)(&tdata)); + + /* Generate a condition group and signal it */ + tdata.phase = 1; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + /* This does not trigger an unwind */ + tdata.phase = 2; + ChfSignal(); + + tdata.phase = 3; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 8, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + /* This MUST trigger an unwind */ + tdata.phase = 4; + ChfSignal(); + + exit(EXIT_FAILURE); + + } + else + { + /* Unwind */ + if(tdata.phase != 5) + exit(EXIT_FAILURE); + + ChfPopHandler(); + } + } + + /* Condition generation and signal while a signal is in progress; + this requires two handlers. + */ + { + tdata.phase = 0; + + ChfPushHandler(h3, NULL, (ChfPointer)&tdata); + ChfPushHandler(h4, NULL, (ChfPointer)&tdata); + + tdata.phase = 1; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + tdata.phase = 2; + ChfSignal(); + + if(tdata.phase != 5) + exit(EXIT_FAILURE); + + ChfPopHandler(); + ChfPopHandler(); + } + + return (void *)0; +} + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; + +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test04"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + +#ifdef _REENTRANT + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +#define H_STACK_SIZE 10 +#define C_STACK_SIZE 30 + +/* Dummy handler; pushed only to verify that the handler stack overflow + checks are correct. +*/ +ChfAction h1( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + return CHF_RESIGNAL; +} + +/* Overflow check handler; it unwinds if the CHF_F_HDLR_STACK_FULL + condition is signalled exactly after H_STACK_SIZE-2 invocations + of ChfPushHandler(), it resignals a modified condition if the + condition is signalled too early +*/ +ChfAction h2( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + int push_count = *((int *)p); + ChfAction action; + + if(s == CHF_SIGNALING) + { + if(ChfGetModuleId(c) == CHF_SET + && ChfGetConditionCode(c) == CHF_F_HDLR_STACK_FULL) + { + /* Handler stack is full; check correctness of the descriptor */ + if(push_count == H_STACK_SIZE-2 + && ChfGetNextDescriptor(c) == NULL + && ChfGetSeverity(c) == CHF_FATAL) + action = CHF_UNWIND; + else + { + ChfCondition 11, CHF_FATAL, push_count, H_STACK_SIZE-2 + ChfEnd; + action = CHF_RESIGNAL; + } + } + } + + else + action = CHF_RESIGNAL; + + return action; +} + +/* Overflow check handler; it unwinds if the CHF_F_COND_STACK_FULL + condition is signalled exactly after C_STACK_SIZE invocations + of ChfCondition, it resignals a modified condition if the + condition is signalled too early +*/ +ChfAction h3( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + int push_count = *((int *)p); + ChfAction action; + + if(s == CHF_SIGNALING) + { + if(ChfGetModuleId(c) == CHF_SET + && ChfGetConditionCode(c) == CHF_F_COND_STACK_FULL) + { + /* Handler stack is full; check correctness of the descriptor */ + if(push_count == C_STACK_SIZE + && ChfGetNextDescriptor(c) == NULL + && ChfGetSeverity(c) == CHF_FATAL) + action = CHF_UNWIND; + else + { + ChfCondition 12, CHF_FATAL, push_count, C_STACK_SIZE + ChfEnd; + action = CHF_RESIGNAL; + } + } + } + + else + action = CHF_RESIGNAL; + + return action; +} + + +void *task(void *arg) +{ + int push_count = 0; + sigjmp_buf jb; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + printf("\tThread %d\n", (int)arg); + + /* Check handler stack overflow checks */ + if(sigsetjmp(jb, 1) == 0) + { + int i; + + /* Push the handler */ + ChfPushHandler(h2, jb, (ChfPointer)(&push_count)); + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + /* Push dummy handlers until an error should occur */ + for(; push_count +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +#define H_STACK_SIZE 10 +#define C_STACK_SIZE 30 + + +void *task(void *arg) +{ + volatile int phase = 0; + + ChfTry + { + phase = 1; + ChfCondition 20, CHF_SUCCESS ChfEnd; + ChfSignal(); + + phase = 2; + ChfCondition 20, CHF_INFO ChfEnd; + ChfSignal(); + + phase = 3; + ChfCondition 20, CHF_WARNING ChfEnd; + ChfSignal(); + + phase = 4; + ChfCondition 20, CHF_ERROR ChfEnd; + ChfSignal(); + + phase = 5; + ChfCondition 20, CHF_FATAL ChfEnd; + ChfSignal(); + + /* Should not be reached */ + return (void *)EXIT_FAILURE; + } + ChfCatch + { + /* Catched an exception; check descriptor */ + const ChfDescriptor *d = ChfGetTopCondition(); + if(d == NULL + || ChfGetNextDescriptor(d) != NULL + || ChfGetModuleId(d) != CHF_MODULE_ID + || ChfGetConditionCode(d) != 20) + return (void *)EXIT_FAILURE; + } + ChfEndTry; + + /* Check that the condition stack actually is empty after catch */ + ChfTry + { + volatile const ChfDescriptor *e = ChfGetTopCondition(); + } + ChfCatch + { + const ChfDescriptor *d = ChfGetTopCondition(); + if(d == NULL + || ChfGetNextDescriptor(d) != NULL + || ChfGetModuleId(d) != CHF_SET + || ChfGetConditionCode(d) != CHF_F_BAD_STATE) + return (void *)EXIT_FAILURE; + } + ChfEndTry; + + return (void *)EXIT_SUCCESS; +} + + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; + +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test06"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", + C_STACK_SIZE, H_STACK_SIZE, EXIT_FAILURE)) + exit(st); + +#ifdef _REENTRANT + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +extern ChfTable message_table[]; +extern size_t message_table_size; + +int main(int argc, char *argv[]) +{ + int st; + const char *msg; + const ChfDescriptor *d, *e; + + puts("test07"); + + /* Initialization */ + if(st = ChfStaticInit(argv[0], CHF_DEFAULT, + message_table, message_table_size, 50, 10, 1)) + exit(st); + + /* ChfGetMessage: + message (CHF_MODULE_ID, 1) exists, (CHF_MODULE_ID, 2) does not + */ + msg = ChfGetMessage(CHF_MODULE_ID, 1, "Default_1"); + if(strcmp(msg, "Set_255,Message_1")) exit(EXIT_FAILURE); + msg = ChfGetMessage(CHF_MODULE_ID, 2, "Default_2"); + if(strcmp(msg, "Default_2")) exit(EXIT_FAILURE); + + /* Generate a condition and check descriptor; this is line 46 */ + ChfCondition 3, CHF_WARNING, 456 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 3 + || d->severity != CHF_WARNING + || d->line_number != 46 + || strcmp(d->file_name, "test07.c") + || strcmp(d->message, "Set_255,Arg_456,Message_3") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Generate another condition and check; this is line 60 */ + ChfCondition 4, CHF_INFO, "arg" ChfEnd; + + if((e = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + e->module_id != CHF_MODULE_ID + || e->condition_code != 4 + || e->severity != CHF_INFO + || e->line_number != 60 + || strcmp(e->file_name, "test07.c") + || strcmp(e->message, "Set_255,Arg_arg,Message_4") + || e->next != d + ) exit(EXIT_FAILURE); + + /* Discard the previous condition group and create a new one */ + ChfDiscard(); + + /* This is line 77 */ + ChfCondition 5, CHF_ERROR, 456, 789 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 5 + || d->severity != CHF_ERROR + || d->line_number != 77 + || strcmp(d->file_name, "test07.c") + || strcmp(d->message, "Set_255,Arg_456-789,Message_5") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Exit Chf */ + ChfExit(); + exit(EXIT_SUCCESS); +} + +ChfTable message_table[] = +{ + { CHF_MODULE_ID, 1, "Set_255,Message_1" }, + { CHF_MODULE_ID, 3, "Set_255,Arg_%d,Message_3" }, + { CHF_MODULE_ID, 4, "Set_255,Arg_%s,Message_4" }, + { CHF_MODULE_ID, 5, "Set_255,Arg_%d-%d,Message_5" } +}; + +size_t message_table_size = sizeof(message_table)/sizeof(message_table[0]); +@ diff --git a/Chf/chf.msf b/Chf/chf.msf new file mode 100644 index 0000000..3318ad3 --- /dev/null +++ b/Chf/chf.msf @@ -0,0 +1,40 @@ +$ .+ +$ . +$ .identifier : $Id: chf.msf,v 2.2 2001/01/25 12:00:19 cibrario Exp $ +$ .context : +$ .title : $RCSfile: chf.msf,v $ +$ .kind : Makefile +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 27-May-1996 +$ .keywords : * +$ .description : +$ . Message source file for the CHF conditions +$ .notes : +$ . $Log: chf.msf,v $ +$ . Revision 2.2 2001/01/25 12:00:19 cibrario +$ . Added partial Win32 support (Windows CE only). +$ . +$ . Revision 2.1 2000/05/26 14:17:21 cibrario +$ . Updated documentation block and RCS log message header to prevent +$ . gencat warnings on Linux boxes +$ . +$ . Revision 1.1 1996/05/28 12:57:06 cibrario +$ . Initial revision +$ . +$ .- + +$set 1 +2 Chf +3 Errno + +$set 2 +1 Condition stack is full +2 Handler stack is full +3 Handler stack is empty +4 Wrong Chf state for requested operation +5 Invalid action code from handler (code=<%d>d) +6 Dynamic memory allocation failed +7 Function not implemented +10 setlocale() failed +11 catopen() failed diff --git a/Chf/chf.rc b/Chf/chf.rc new file mode 100755 index 0000000..daa575c --- /dev/null +++ b/Chf/chf.rc @@ -0,0 +1,99 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + 1002 "Chf" +END + +STRINGTABLE DISCARDABLE +BEGIN + 2001 "Condition stack is full" + 2002 "Handler stack is full" + 2003 "Handler stack is empty" + 2004 "Wrong state for requested operation" + 2005 "Invalid action code from handler (code=<%d>d)" + 2006 "Dynamic memory allocation failed" + 2007 "Function not available" + 2010 "setlocale() failed" + 2011 "catopen() failed" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Italian (Italy) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ITA) +#ifdef _WIN32 +LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Italian (Italy) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Chf/chf.texi b/Chf/chf.texi new file mode 100644 index 0000000..6201eaf --- /dev/null +++ b/Chf/chf.texi @@ -0,0 +1,2053 @@ +\input texinfo @c -*-texinfo-*- + +@c $Id: chf.texi,v 2.2 2001/01/25 12:06:29 cibrario Exp $ +@c %**start of header +@setfilename chf.info +@settitle Chf library (libChf r2.2) Documentation +@setchapternewpage on +@iftex +@afourpaper +@end iftex +@c %**end of header + +@ifinfo +This manual documents the Condition Handling Facility library (libChf r2.2). + +The Chf library provides a centralized, unified method of generating and +signaling exceptional conditions, establishing exception handlers, and +printing status and error messages. +@end ifinfo + +@titlepage +@title CHF library Documentation +@subtitle Condition Handling Facility library +@subtitle (libChf r2.2) +@author Ivan Cibrario B. +@end titlepage + +@node Top, Overview, (dir), (dir) + +@ifinfo +This manual describes the Condition Handling Facility library, and +applies to its release 2.2. +@end ifinfo + +@menu +* Overview:: +* Initialization and exit:: +* Condition generation and signal:: +* General condition handling:: +* Structured condition handling:: +* Chf conditions:: +* Message retrieval:: +* Multithreading support:: +* Other useful macro definitions:: +* Other useful functions:: +* Changes since release 1:: +* Changes since release 2.1:: +* Function Index:: +* Data Type Index:: +* Concept Index:: + +@c @detailmenu --- The Detailed Node Listing --- + +Initialization and exit + +* ChfInit():: +* ChfMsgcatInit():: +* ChfStaticInit():: +* ChfWin32Init():: +* ChfExit():: +* ChfAbort():: +* Library header:: + +Condition generation and signal + +* Generating a condition:: +* Signaling a condition:: + +Generating a condition + +* ChfCondition:: +* ChfEnd:: +* ChfSeverity:: +* ChfErrnoCondition:: + +Signaling a condition + +* ChfSignal():: + +Condition handling + +* Condition handlers:: +* Condition descriptors:: +* Condition handling state:: +* Handler actions:: +* Default handler:: + +Condition handlers + +* ChfHandler:: +* ChfPushHandler():: +* ChfPopHandler():: + +Structured condition handling + +* Syntax of structured condition handlers:: +* Chf behavior during structured condition handling:: + +Chf behavior during structured condition handling + +* ChfTry:: +* ChfCatch:: +* ChfEndTry:: + +Message retrieval + +* Retrieving a message:: +* Reserved module identifiers:: +* Defining a new message retrieval subsystem:: + +Retrieving a message + +* ChfGetMessage():: +* ChfBuildMessage():: + +@c @end detailmenu +@end menu + +@node Overview, Initialization and exit, Top, Top +@chapter Overview +@cindex Overview +@cindex Win32 Support + +The Condition Handling facility library +provides a centralized, unified method of generating and +signaling exceptional conditions. This method can be used either in +conjunction with, or in alternative to, the traditional style of handling +error conditions by returning distinguished error values when an error +occurs in a procedure, and checking for these values after each procedure +invocation. + +It serves many different purposes: + +@itemize @bullet + +@item +It helps to gather and make available as much information as possible when +an error occurs, such as which source module detected it, its severity, +and any other ancillary information the user may find useful. + +@item +It provides an unified method to log and print error messages, and to +internationalize applications. + +@item +By means of condition handlers, it gives the user a convenient way to +implement global exception recovery actions in an application. + +@item +It may improve the readability of the source code, because less explicit +checks for error conditions are required. + +@end itemize + +Since release @code{2.2}, the Chf library also supports Unicode-based Win32 +systems, notably Windows CE. Implementation details of the Win32 version +of the library will be discussed in the documentation, and will be +highlighted by the @strong{Win32} keyword. Moreover, the following +general notes apply: + +@itemize @bullet + +@item +All library functions, when compiled for Win32, handle Unicode +character strings (array of @code{TCHAR}) instead of ASCII strings +(array of @code{char}); this change is transparent and the documentation +does not reflect it. + +@item +Library functions @code{sigsetjmp()} and @code{siglongjmp()} are not +supported; accordingly, they are replaced by +@code{setjmp()} and @code{longjmp()}. The non-local control transfer +context buffer, @code{sigjmp_buf} is also replaced by @code{jmp_buf}; +this change is transparent and the documentation does not reflect it. + +@item +The reentrant flavor of the Chf library is not yet supported. Multithreaded +application must serialize Chf accesses themselves. + +@item +The @code{ChfErrnoCondition} macro is not supported, and has no effect. + +@item +The default condition handler does not display the condition message +on the standard error stream. + +@item +The @code{CHF_ABORT} initialization flag is not fully supported; +when used, the Chf library invokes @code{exit(EXIT_FAILURE)} +instead of @code{abort()} (not supported by Win32) to abort the application. + +@end itemize + +The following terms will be used often throughout this document: + +@table @code +@item Exception +@cindex Exception +An @dfn{exception} is an event that changes the normal flow of instruction +execution, and is usually caused by an error condition. Any procedure can +generate an exception using the @code{ChfGenerate} macro, and then +raise or signal it using the @code{ChfSignal()} function call. + +@item Condition +@cindex Condition +An informational state which exists when an exception occurs, and encloses +information about the exception itself. The term @dfn{condition} is preferred, +since the term exception usually implies an error. + +@item Condition handling +@cindex Condition handling +When a condition is detected during the execution of a procedure, the +procedure can signal the condition. The +hierarchy of currently active condition handlers is then permitted to +respond to the condition; the procedure response is called +@dfn{condition handling}. + +The condition handlers are themselves procedures and they are called +with the usual calling conventions; the only exception is that, in the +current release of the Chf library, they cannot establish condition +handlers of their own. + +A procedure can establish a condition handler using the function +@code{ChfPushHandler()}, that pushes a new condition handler on top of +a LIFO handler stack; the condition handler stays active until a +matching call to @code{ChfPopHandler()}, that pops the topmost handler +from the handler stack is executed, or until an execution stack +unwind occurs. + +Another way to implicitly establishing and removing condition handlers +is to use the structured condition handling macros. +@xref{Structured condition handling}, for more information. + +@end table + +@node Initialization and exit, Condition generation and signal, Overview, Top +@chapter Initialization and exit +@cindex Initialization and exit +@cindex Initialization +@cindex Exit + +Before using any function of the Chf library, it must be correctly +initialized. Since the library has a generic interface towards the +message retrieval subsystem (@pxref{Message retrieval}), +more than one initialization function exist, one each for every message +retrieval subsystem the library supports, plus one custom initialization +function to use if a custom message retrieval subsystem is used. +After initialization, the library accesses the message retrieval subsystem +transparently, and its peculiarities are no longer visible to the users. + +All initialization functions, when successful, register a default condition +handler. @xref{Default handler}. + +Moreover, each module using the Chf library must include the header file +@code{Chf.h} and provide some basic definitions. @xref{Library header}. + +@menu +* ChfInit():: +* ChfMsgcatInit():: +* ChfStaticInit():: +* ChfWin32Init():: +* ChfExit():: +* ChfAbort():: +* Library header:: +@end menu + +@node ChfInit(), ChfMsgcatInit(), Initialization and exit, Initialization and exit +@section ChfInit() +@cindex Initialization +@deftypefn Function int ChfInit (const char *app_name, const ChfOptions options, void *mrs_data, ChfMrsGet mrs_get, ChfMrsExit mrs_exit, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library using a custom message retrieval +subsystem, and returns to the caller a condition code; that code will be either +@code{CHF_S_OK} if the initialization was successful, or one of the other +values listed below. + +Arguments: + +@table @code +@item app_name +is the name of the application. It can be used by condition +handlers to print out error messages. + +@item options +is the bitwise or of the following option identifiers: + +@table @code + +@item CHF_DEFAULT +Represents the default value for all options. + +@item CHF_ABORT +If this flag is set, the Chf library will use @code{abort()} instead of +either @code{exit(exit_code)} or @code{pthread_exit(exit_code)} to abort +the application when an unrecoverable exception occurs. This forces a +core dump that may be useful for debugging purposes. + +@strong{Win32:} This flag is not fully supported; the Chf library +invokes @code{exit(EXIT_FAILURE)} instead of @code{abort()} (not +supported by Win32) to abort the application. + +@end table + +@item mrs_data +Private data pointer for the custom message retrieval subsystem. + +@item mrs_get +Message retrieval subsystem's 'Get Message' function. + +@item mrs_exit +Message retrieval subsystem's 'Exit' function. + +@item condition_stack_size +Size of the condition stack. This value determines the maximum number of +pending conditions, i.e. conditions that have been generated but have +not been signaled yet. A condition that is being signaled is +still pending until the signaling sequence completes. + +@item handler_stack_size +Size of the handler stack. This value determines the maximum number of +active condition handlers the application can have. A condition handler is +activated when a @code{ChfPushHandler()} is executed for it, and it is +deactivated when the corresponding @code{ChfPopHandler()} completes. +In addition, each execution of the @code{ChfTry} macro also pushes +a condition handler; the handler is removed when @code{ChfEndTry} +is executed. + +@item exit_code +This is the exit code the Chf library will use to exit either the +application or the offending thread when an unrecoverable exception +occurs. + +@end table + +Return values: + +@table @code + +@item CHF_S_OK +The Chf library has been successfully initialized. + +@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@end table + +Notes: + +@itemize @bullet + +@item +It's necessary to invoke successfully @code{ChfInit()}, either directly +or indirectly, through another Chf initialization function, before using +any other Chf function; otherwise, they will either fail and abort the +application (when multithreading support is not enabled), or have +unpredictable results (when multithreading support is enabled). + +@item +This function will call @code{ChfAbort()} with abort code +@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. Notice that when multithreading support is enabled, @code{ChfInit()} +must be invoked only once before starting multithreaded operation. + +@end itemize +@end deftypefn + +@node ChfMsgcatInit(), ChfStaticInit(), ChfInit(), Initialization and exit +@section ChfMsgcatInit() +@cindex Initialization +@cindex Internationalization +@deftypefn Function int ChfMsgcatInit (const char *app_name, const ChfOptions options, const char *msgcat_name, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library and activates the message retrieval +subsystem based on @code{catgets()}, offering support for +text-based internationalized applications. + +The argument @code{msgcat_name} is the name of the application/locale +specific message catalog to be used to retrieve the messages. The other +arguments have the meaning already described for @code{ChfInit()}, +@xref{ChfInit()}. + +@strong{Win32:} This function is not supported and always returns +@code{CHF_F_NOT_AVAILABLE}. + +Return values: + +@table @code + +@item CHF_S_OK +The Chf library has been successfully initialized. + +@item CHF_F_SETLOCALE +the @code{setlocale()} function has failed during initialization. + +@item CHF_F_CATOPEN +the @code{catopen()} function has failed and therefore the message catalog was +not opened. + +@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@item CHF_F_NOT_AVAILABLE +Function not available on current platform. + +@end table + +Notes: + +@itemize @bullet + +@item +It's necessary to invoke successfully either @code{ChfMsgcatInit()}, +or another Chf initialization function described in this chapter, +before using any other Chf function. + +@item +This function will call @code{ChfAbort()} with abort code +@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. + +@end itemize +@end deftypefn + +@node ChfStaticInit(), ChfWin32Init(), ChfMsgcatInit(), Initialization and exit +@section ChfStaticInit() +@cindex Initialization +@deftypefn Function int ChfStaticInit (const char *app_name, const ChfOptions options, const ChfTable *table, const size_t table_size, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library and activates the static +message retrieval subsystem using the message table @code{table}, +of @code{table_size} elements. This is the simplest message retrieval +subsystem currently supported, and supports applications that must work +with minimal operating system assistance. + +Return values: + +@table @code + +@item CHF_S_OK +The Chf library has been successfully initialized. + +@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@end table + +Notes: + +@itemize @bullet + +@item +It's necessary to invoke successfully either @code{ChfStaticInit()}, +or another Chf initialization function described in this section, +before using any other Chf function. + +@item +This function will call @code{ChfAbort()} with abort code +@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. + +@end itemize +@end deftypefn + +@deftp {Data type} ChfTable +@cindex Standalone message table +This data type is a structure representing an element of a standalone message +table, to be used with the static message retrieval subsystem. Each table +element associates a pair (@code{module_id}, @code{code}) with a message +fragment or template. It has the following fields: + +@table @code +@item int module +The module identifier of the message fragment to be defined. + +@item int code +The condition code of the message fragment to be defined. + +@item char *msg_template +The message fragment or template associated with +(@code{module_id}, @code{code}). + +@end table +@end deftp + +@node ChfWin32Init(), ChfExit(), ChfStaticInit(), Initialization and exit +@section ChfWin32Init() +@cindex Initialization +@cindex Internationalization +@deftypefn Function int ChfWin32Init (const char *app_name, const ChfOptions options, HINSTANCE instance, const int condition_stack_size, const int handler_stack_size, const int exit_code) + +This function initializes the Chf library and activates the message +retrieval subsystem based on the Win32 function @code{LoadString()}, +offering limited support for Win32 internationalized applications. + +This function does not support message sets, so the linear message id +passed to it is made by @code{module_id*1000 + condition_code}. + +The @code{instance} argument is the handle of the calling application +instance; it will be used as an argument to @code{LoadString()}. All +other arguments have the meaning already described for @code{ChfInit()}, +@xref{ChfInit()}. + +@strong{Win32:} This function is supported on Win32 only, and always +returns @code{CHF_F_NOT_AVAILABLE} when invoked on Unix platforms. +Notice also that on Win32 the @code{ChfAbort()} function will be able to +print out the abort message only if Chf has been initialized correctly +and the @code{GetActiveWindow()} function yields a valid window handle +when @code{ChfAbort()} is invoked. + +Return values: + +@table @code + +@item CHF_S_OK +The Chf library has been successfully initialized. + +@item CHF_F_MALLOC +Dynamic memory allocation failed during initialization. + +@item CHF_F_NOT_AVAILABLE +Function not available on current platform. + +@end table + +Notes: + +@itemize @bullet + +@item +It's necessary to invoke successfully either @code{ChfWin32Init()}, +or another Chf initialization function described in this chapter, +before using any other Chf function. + +@item +This function will call @code{ChfAbort()} with abort code +@code{CHF_ABORT_DUP_INIT} if the Chf library has already been initialized +before. + +@end itemize +@end deftypefn + +@node ChfExit(), ChfAbort(), ChfWin32Init(), Initialization and exit +@section ChfExit() +@cindex Exit +@deftypefn Function void ChfExit (void) + +This function shuts down the Chf library and returns nothing to the caller; +after calling @code{ChfExit()} the application can continue, but any +subsequent call to other Chf functions, +except to the initialization functions, will either abort +the application using @code{ChfAbort()} with abort code @code{CHF_ABORT_INIT} +(when multithreading support is not enabled) or have unpredictable results +(when multithreading support is enabled). + +Notes: + +@itemize @bullet +@item +This function will call @code{ChfAbort()} with abort code @code{CHF_ABORT_INIT} +if Chf hasn't been initialized yet. + +@item +When multithreading support is enabled, it is responsibility of the +application designer to ensure that no other Chf functions, +except initialization functions, will be +called after this function has been. +@end itemize +@end deftypefn + +@node ChfAbort(), Library header, ChfExit(), Initialization and exit +@section ChfAbort() +@cindex Abort +@deftypefn Function void ChfAbort (const int abort_code) +This function prints the message associated with @code{abort_code} and +then immediately aborts either the application or the invoking +thread. The abort is performed either: + +@itemize @bullet +@item +using @code{abort()} if either Chf has not been correctly initialized +yet, or the Chf option flag @code{CHF_ABORT} has been set during +initialization. Since this is mainly intended to be a debugging aid, +@code{abort()} is used whether or not multithreading support is enabled. +@strong{Win32:} The @code{CHF_ABORT} flag is not fully supported; when +this flag is set the Chf library invokes @code{exit(EXIT_FAILURE)} instead +of @code{abort()} (not supported by Win32) to abort the application. +@item +using either @code{exit(exit_code)} or @code{pthread_exit(exit_code)} +if the above mentioned flag is clear. The former function is used when +multithreading support is not enabled, the latter when it is. The +argument @code{exit_code} is the value of the @code{exit_code} argument +given to the Chf initialization function. +@end itemize + +The following abort messages are currently defined: + +@table @code +@item "Not initialized" +The Chf library has not been correctly initialized. +@item "Temporary message buffer overflow" +The temporary message buffer used by Chf to combine the additional condition +arguments with the message template was overflowed. Shorten the message. +@item "Invalid action from last chance handler" +The condition handler registered first, that usually is the default +condition handler, has returned an invalid action code. +@item "Already initialized" +One of the Chf initialization functions was called when Chf was already +initialized. +@item "Unwind request while unwinding" +A condition handler has requested an unwind action when Chf was already +unwinding. This is not allowed. +@item "Improperly handled condition" +No condition handlers were able to properly handle the current condition +group. This should not occur if the default condition handler is active +as the last chance handler. +@item "Fatal condition while unwinding" +A fatal condition was signaled while Chf was unwinding. This is not +allowed because is would require another unwind to be recovered. +@item "Condition stack overflow" +An attempt was made to generate a condition while the Chf condition +CHF_F_COND_STACK_FULL (condition stack full) was being signaled. +@item "Can't prime a new Chf context" +Chf was unable to clone the master Chf context when a thread +first attempted to access its context. This message is generated +only when multithreading support is enabled. +@item "Pthread interaction failed" +An unrecoverable error occurred when Chf invoked a @code{pthread} +function. This message is generated only when multithreading support +is enabled. +@end table + +No message is printed if the @code{abort_code} is @code{CHF_ABORT_SILENT}; +this code is used, for example, by the default condition handler to terminate +the application when a @code{CHF_FATAL} condition occurs. + +Notes: + +@itemize @bullet +@item +This function is called by other Chf library functions only when they +detect an unrecoverable failure. Even if it has a public prototype, the user +should never invoke this function. +@item +@strong{Win32:} Since the standard error stream is not available, this +function is able to display the abort message only if it is able to +obtain a valid main window handle, that is, if the +@code{GetActiveWindow()} function yields a valid value when invoked. +@end itemize + +@end deftypefn + +@node Library header, , ChfAbort(), Initialization and exit +@section Library header +@cindex Library header + +The library header @code{Chf.h} must be included by all source modules using +the Chf library. Prior to include it, the following macro definitions must +be made: + +@table @code +@item CHF_MODULE_ID +This macro must expand into an integer representing the module identifier +of the module the source file belongs. The module identifiers with value +less than 10 are reserved for Chf internal use. +@xref{Reserved module identifiers}. + +All conditions generated by the source module will use this module +identifier. Even if this macro could in principle be redefined through the +source, it is generally inconvenient to have functions belonging to different +application's modules in the same source file, so it is discouraged. +This definition is mandatory. + +@item CHF_EXTENDED_INFO +If this macro is set, all conditions generated by the source module +will include the file name and the line number as additional information +items. This can be useful for debugging purposes. This definition is +optional. +@end table + +@strong{Win32:} The @code{tchar.h} system header must be included +before including @code{Chf.h}. + +@node Condition generation and signal, General condition handling, Initialization and exit, Top +@chapter Condition generation and signal +@cindex Condition generation and signal + +@menu +* Generating a condition:: +* Signaling a condition:: +@end menu + +@node Generating a condition, Signaling a condition, Condition generation and signal, Condition generation and signal +@section Generating a condition +@cindex Generating a condition + +When an unusual or noteworthy condition occurs, the application may generate +a condition that describes it. + +@menu +* ChfCondition:: +* ChfEnd:: +* ChfSeverity:: +* ChfErrnoCondition:: +@end menu + +@node ChfCondition, ChfEnd, Generating a condition, Generating a condition +@subsection ChfCondition +@deftypefn Macro {} ChfCondition {...} + +This macro starts the definition of a condition using the current +application's module specifier @code{CHF_MODULE_ID}; it must be followed by +these additional, mandatory, arguments: + +@table @code +@item int condition_code +This argument is the condition code; it uniquely identifies a condition +within the scope of the current application's module. + +@item ChfSeverity severity +This value represents the severity of the condition. @xref{ChfSeverity}. +@end table + +Moreover, zero or more additional argument can follow; they will be +immediately combined with the message template corresponding to the +pair (@code{CHF_MODULE_ID}, @code{condition_code}) using @code{sprintf()}. +All format specifiers of the latter function are allowed in the message +template. + +As stated before, the message template is retrieved using the message +retrieval subsystem (@pxref{Message retrieval}) using the current Chf +module identifier @code{CHF_MODULE_ID} as the message set, and the +condition code @code{condition_code} as the message number. + +If in the condition stack there isn't any condition yet, a new condition group +is created, otherwise the new condition is linked on top of the current +condition group. The condition stack supports multiple nested condition +groups for use in condition handlers: any condition generated inside a +condition handler starts a new condition group that can be individually +handled and signaled. + +This function calls the Chf function @code{ChfAbort()} if one of the following +unrecoverable error condition occurs: + +@itemize @bullet +@item +Chf has not been initialized correctly (abort code @code{CHF_ABORT_INIT}) + +@item +there is an overflow in the internal buffer used during the +generation of the partial message associated with the condition +(abort code @code{CHF_ABORT_MSG_OVF}) + +@item +there was an attempt to generate a condition while the Chf condition +@code{CHF_F_COND_STACK_FULL} (condition stack full) was being signaled +(abort code @code{CHF_ABORT_COND_STACK_OVF}) + +@end itemize +@end deftypefn + + +@node ChfEnd, ChfSeverity, ChfCondition, Generating a condition +@subsection ChfEnd +@deftypefn Macro {} ChfEnd + +This macro terminates the generation of the condition, started +with @code{ChfCondition}; it must be followed by @code{;}. For example: + +@example +ChfCondition + , , +ChfEnd; +@end example + +@end deftypefn + +@c 2.1, cibrario, 26-May-2000 +@c Expanded description of severity codes +@node ChfSeverity, ChfErrnoCondition, ChfEnd, Generating a condition +@subsection ChfSeverity +@deftp {Data type} ChfSeverity +The severity code of a condition is an enumerated type, and can assume one +of the following values: + +@table @code +@item CHF_SUCCESS +This condition represents the successful completion of an application's +action; when generated in an application's module it is usually signaled +immediately after generation, it is not reported to the module caller +and does not disrupt the normal control flow of the application. + +@item CHF_INFO +This condition conveys an information message to the application's user, +but it in no way represents an error indication; when generated in an +application's module it is usually signaled immediately after +generation, it is not reported to the module caller and does not disrupt +the normal control flow of the application. + +@item CHF_WARNING +This condition has been generated to warn the application's user that a +potentially abnormal situation occurred, but the application's module +was able to complete successfully the action that was requested to it, +perhaps incompletely. It is usually not signaled immediately after +generation, it is reported to the caller using some status reporting +mechanism, and it requires only a minimal, local disruption of the +control flow of the application to be recovered. + +@item CHF_ERROR +An error occurred and the application's module was not able to complete its +work, but it still may be possible to perform a local recovery and resume the +normal application's control flow. It is usually not signaled immediately +after generation and it is reported to the caller using some status +reporting mechanism. + +@item CHF_FATAL +A fatal error occurred, and a global recovery action is required, because +the normal application's control flow can't be safely resumed. The Chf library +enforces that, when @code{ChfSignal()} is called with a fatal condition, the +control will never return to the instruction following the @code{ChfSignal()} +call. It is usually signaled immediately after generation. + +@end table +@end deftp + +@node ChfErrnoCondition, , ChfSeverity, Generating a condition +@subsection ChfErrnoCondition +@deftypefn Macro {} ChfErrnoCondition + +This macro generates a condition from the current value of the @code{errno} +variable. The module identifier of the condition is set to the special +value @code{CHF_ERRNO_SET}. The severity is always @code{CHF_ERROR}. + +@strong{Win32:} This macro is not supported and has no effect when invoked. + +@end deftypefn + + +@node Signaling a condition, , Generating a condition, Condition generation and signal +@section Signaling a condition +@cindex Signaling a condition + +A condition already generated with one of the macros discussed in the +previous section can be signaled using the following function, that starts +the condition signaling sequence. + +@menu +* ChfSignal():: +@end menu + +@node ChfSignal(), , Signaling a condition, Signaling a condition +@subsection ChfSignal() +@cindex ChfSignal() +@deftypefn Function void ChfSignal (void) +@end deftypefn + +This function signals the current condition group. If the final action +requested by the condition handlers invoked during the signaling operation +was @code{CHF_CONTINUE} and the overall severity of the condition group +was lower than @code{CHF_FATAL}, this function will return to the caller, +otherwise either a non-local control transfer will occur (@code{CHF_UNWIND}) +or the invoking application/thread will be terminated. + +During the signaling of a condition group the following actions are +accomplished: + +@table @bullet +@item +First of all, a check is made to ensure that the Chf subsystem has been +correctly initialized. If not, the function @code{ChfAbort} is called. +@item +The state of Chf is changed to @code{CHF_SIGNALING} if it was @code{CHF_IDLE}, +and to @code{CHF_SIGNAL_UNWINDING} if it was @code{CHF_UNWINDING}. +@item +If there is not any active condition group, the function does nothing more, +restores the saved Chf state and returns to the caller. +@item +If there is an active condition group, the currently active condition handlers +are invoked, starting from the one registered last. +@item +Each condition handler can recursively invoke @code{ChfGenerate()} and +@code{ChfSignal()}. @code{ChfGenerate()}, when invoked, will create a new +condition group, and @code{ChfSignal()} will signal the condition group +just created. +@item +When the Chf state is @code{CHF_SIGNALING}, any new condition group generated +but not yet signaled when the current handler exits is merged +with the condition group currently being signaled, in order to allow +the condition handlers to add their own conditions to the condition +group. If the severity of the previous condition group was @code{CHF_FATAL}, +the severity of the new group is forced to @code{CHF_FATAL}, too. +When the Chf state is @code{CHF_UNWINDING}, the condition group for +which the unwind has been requested is 'frozen', no further +modifications are allowed on it, and the new condition group is simply +discarded. +@item +The condition handlers calling sequence terminates when either the handler +stack is exhausted (all condition handlers have been called) or when an +handler requests either the action @code{CHF_CONTINUE}, +@code{CHF_UNWIND} or @code{CHF_UNWIND_KEEP}. +Please note that the @code{CHF_CONTINUE} action cannot be requested if +the severity of the condition group is @code{CHF_FATAL}; when this occurs, +the action is automatically changed to @code{CHF_RESIGNAL} instead. +@item +If the action @code{CHF_CONTINUE} has been requested and it is allowed, Chf +discards the current condition group, restored the saved Chf state and +returns to the instruction following the call to @code{ChfSignal()}, that +started the signaling activities. +@item +If either one of the actions @code{CHF_UNWIND} or @code{CHF_UNWIND_KEEP} +has been requested and it is allowed, that is, +no other unwinds are already in progress, the current Chf state is set +to @code{CHF_UNWINDING} and all handlers that were registered +after the handler that requested the unwind are called again, starting +from the one registered last, so that they can perform any final cleanup +operation they require for proper operation. The handler that requested +the unwind is called again, too, in the same fashion. Immediately after +this, the unwinded condition handlers are removed from the handler stack +and discarded, the current condition group and any other group generated +after it are destroyed, the Chf state is changed back to @code{CHF_IDLE} +and a non-local control transfer is executed through a +@code{siglongjmp()}, using the @code{unwind_context} associated with the +handler that requested the unwind as target. The @code{ChfSignal()} +invocation that started the signaling activities will never return to +the caller. If the requested action is @code{CHF_UNWIND_KEEP} everything +goes as described above, except that the current condition group and +any other group generated after it are @strong{not} destroyed, so they can +still be accessed after the non-local control transfer has taken place. +@item +If no handlers were able to handle the condition, the invoking +application/thread is terminated with @code{ChfAbort()}. +@item +If an handler returned an invalid action code and it is not the condition +handler that was registered first, a new condition group is generated and +immediately signaled, containing the condition @code{CHF_F_INVALID_ACTION} +with module identifier @code{CHF_SET}. +@end table + +@c 2.1, cibrario, 18-May-2000 +@c Renamed 'condition handling' -> 'general condition handling' +@node General condition handling, Structured condition handling, Condition generation and signal, Top +@chapter Condition handling +@cindex Condition handling + +@menu +* Condition handlers:: +* Condition descriptors:: +* Condition handling state:: +* Handler actions:: +* Default handler:: +@end menu + +@node Condition handlers, Condition descriptors, General condition handling, General condition handling +@section Condition handlers +@cindex Condition handlers + +The Chf library maintains a LIFO stack of active condition handlers. Each +condition handler is a function which has type @code{ChfHandler}. +Moreover, the functions described below are available to push and pop +a condition handler to/from the condition handler stack. + +@menu +* ChfHandler:: +* ChfPushHandler():: +* ChfPopHandler():: +@end menu + +@node ChfHandler, ChfPushHandler(), Condition handlers, Condition handlers +@subsection ChfHandler +@cindex ChfHandler +@deftp {Data Type} ChfHandler + +The condition handler data type is defined as follows: + +@example +typedef /* Condition handler */ + ChfAction (*ChfHandler)( + const ChfDescriptor *, + const ChfState, + ChfPointer +); +@end example + +The condition handler receives the following arguments when invoked: + +@table @code + +@item const ChfDescriptor * +is a pointer to the condition group that is being signaled. + +@item const ChfState +represents the current state of the condition handling facility. + +@item ChfPointer +is a copy of the @code{handler_context} pointer passed to the +@code{ChfPushHandler()} invocation that established the condition +handler. + +@end table + +The handler must return an action code, represented by a value +of type @code{ChfAction}. + +Before returning, the condition handler can generate other conditions, +without signaling them, with the purpose of better specify the current +condition group; these conditions will be added on top of the current +group when the handler returns. The next handler, if any, will be invoked +on the updated condition group. + +The severity @code{CHF_FATAL} is 'sticky', that is, any new condition +linked to a condition group where the topmost condition has that +severity will be forced to the same severity level. + +If the condition handler itself generates and signals a new condition +group, its superior condition handlers will be invoked on the new +condition group only. When the new signaling sequence is completed, +the old sequence will be resumed if appropriate, i.e. if a superior +handler has requested the @code{CHF_CONTINUE} action and the severity +of the new condition group allows it to do so. + +@end deftp + + +@node ChfPushHandler(), ChfPopHandler(), ChfHandler, Condition handlers +@subsection ChfPushHandler() +@cindex Condition handlers +@cindex Adding condition handlers +@deftypefn Function void ChfPushHandler (ChfHandler new_handler, void *unwind_context, ChfPointer handler_context) + +This function pushes the new condition handler @code{new_handler} with +its associated @code{siglongjmp()} context, pointed by +@code{unwind_context}, into the handler stack. If @code{new_handler} is +@code{CHF_NULL_HANDLER}, the special builtin structured condition +handling helper is pushed; see @ref{Structured condition handling}, for +further information. + +Moreover, this function saves a copy of the opaque pointer +@code{handler_context}; it will be passed to @code{new_handler} upon each +subsequent activation, and therefore can be used as a private handler context +pointer. The user must ensure that the information pointed by +@code{handler_context}, if any, will remain valid until @code{new_handler} +is popped from the condition stack. + +The pointer @code{handler_context} may be set to the special (null) value +@code{CHF_NULL_POINTER} to indicate that the handler hasn't any private +context information. + +If, in the future, the handler will request the @code{CHF_UNWIND} +action, the @code{sigsetjmp()} function invocation that established +@code{unwind_context} will appear to return again. + +The pointer @code{unwind_context} can be the reserved (null) pointer +@code{CHF_NULL_CONTEXT}; in this case, if the handler will request the +@code{CHF_UNWIND} action, the invoking application/thread will be +silently terminated calling @code{ChfAbort()} with abort code +@code{CHF_ABORT_SILENT}. + +If an error occurs during the execution, the function generates +and immediately signals one of the conditions listed below; the function +will never return directly to the caller, since all conditions have +severity @code{CHF_FATAL}. + +@table @code + +@item CHF_F_BAD_STATE +Bad Chf state for requested operation. A new handler can be added to the +handler stack only if the condition handling subsystem is idle. + +@item CHF_F_HDLR_STACK_FULL, +The handler stack is full and the new handler can't be added. + +@end table + +Notes: + +@itemize @bullet + +@item +This function will call @code{ChfAbort()} with abort code @code{CHF_ABORT_INIT} +if Chf hasn't been initialized. + +@end itemize +@end deftypefn + +@node ChfPopHandler(), , ChfPushHandler(), Condition handlers +@subsection ChfPopHandler() +@cindex Condition handlers +@cindex Removing condition handlers +@deftypefn Function void ChfPopHandler (void) + +This function pops the topmost condition handler from the handler stack and +returns to the caller. + +If an error occurs during the execution, the function generates +and immediately signals one of the conditions listed below; the function +will never return directly to the caller, since all conditions have +severity @code{CHF_FATAL}. + +@table @code + +@item CHF_F_BAD_STATE +Bad Chf state for requested operation. Handlers can be removed from the +handler stack only if the condition handling subsystem is idle. + +@item CHF_F_HDLR_STACK_EMPTY +The handler stack is empty. + +@end table + +Notes: + +@itemize @bullet + +@item +This function will call @code{ChfAbort()} with abort code @code{CHF_ABORT_INIT} +if Chf hasn't been initialized. + +@end itemize + +@end deftypefn + + +@node Condition descriptors, Condition handling state, Condition handlers, General condition handling +@section Condition descriptors +@cindex Condition descriptors +@deftp {Data Type} ChfDescriptor + +The condition descriptor contains all information Chf has about a +condition code. Its fields must not be accessed directly, but using +the macros the Chf library provides for this purpose. + +@end deftp + +The following macros are provided to access a condition descriptor: + +@deftypefn Macro {ChfDescriptor *} ChfGetNextDescriptor (Chf Descriptor *d) +@cindex Next condition descriptor + +Returns to the caller the condition descriptor that hierarchically +follows @code{d} in the current condition group, or the special +value @code{CHF_NULL_DESCRIPTOR} if @code{d} is the last descriptor of the +group. + +@end deftypefn + +@deftypefn Macro int ChfGetModuleId (Chf Descriptor *d) +@cindex Module identifier + +Returns to the caller the identifier of the module that generated +the condition @code{d}. + +@end deftypefn + +@deftypefn Macro int ChfGetConditionCode (Chf Descriptor *d) +@cindex Condition Code + +Returns to the caller the condition code of the condition @code{d}. + +@end deftypefn + +@deftypefn Macro ChfSeverity ChfGetSeverity (Chf Descriptor *d) +@cindex Severity + +Returns to the caller the severity level of the condition @code{d}. +@xref{ChfSeverity}. + +@end deftypefn + +@deftypefn Macro int ChfGetLineNumber (Chf Descriptor *d) +@cindex Line number + +Returns to the caller the source line number where the condition @code{d} +was generated. This piece of information is available only if the +Chf compile-time option @code{CHF_EXTENDED_INFO} was set when the source +file that generated the condition was compiled, otherwise the special +value @code{CHF_UNKNOWN_LINE_NUMBER} is returned instead. + +@end deftypefn + +@deftypefn Macro {const char *} ChfGetFileName (Chf Descriptor *d) +@cindex File name + +Returns to the caller the file name where the condition @code{d} +was generated. This piece of information is available only if the +Chf compile-time option @code{CHF_EXTENDED_INFO} was set when the source +file that generated the condition was compiled, otherwise the special +value @code{CHF_UNKNOWN_FILE_NAME} is returned instead. + +@end deftypefn + +@deftypefn Macro {char *} ChfGetPartialMessage (Chf Descriptor *d) +@cindex Partial condition message + +Returns to the caller the partial condition message that was associated +with the condition @code{d} when it was generated. The partial condition +message contains the result of the execution of @code{sprintf()} using +the message template associated with the condition as format, and the +ancillary condition arguments as additional arguments. + +@end deftypefn + + + +@node Condition handling state, Handler actions, Condition descriptors, General condition handling +@section Condition handling state +@cindex Condition handling state +@deftp {Data Type} ChfState +This enumerated type describes the current state of the Condition Handling +Facility. It can have one of the following values: + +@table @code +@item CHF_UNKNOWN +The Chf library has not been initialized, or the initialization has failed. +Normally, the user never sees this value, because the main use of +@code{ChfState} values is inside condition handlers, and they are never +invoked when the Chf library has not been initialized correctly. + +@item CHF_IDLE +This is the normal state of the condition handling facility. In this state, +the condition stack contains zero or more condition descriptors corresponding +to conditions that have been generated but have not been signaled yet. +The application follows its normal control flow. + +@item CHF_SIGNALING +The Condition Handling Facility is invoking the hierarchy of active +condition handlers because @code{ChfSignal()} has been invoked with a +non-empty condition stack. None of the condition handlers invoked so far +has requested an unwind operation. + +@item CHF_UNWINDING +The Condition Handling Facility is unwinding one or more condition handlers, +because another, superior, handler has requested an unwind operation. +All handlers being unwinded are called once with Chf in this state to allow +them to perform any final cleanup operation, and immediately after that +they are removed from the condition stack. During unwind, +the action code returned by the handler being unwinded is ignored. + +@item CHF_SIGNAL_UNWINDING +While Chf was performing an unwind operation, one of the handlers being +unwinded has signaled another condition group. The superiors of the +signaling handler are invoked with Chf in this state, then the unwind +operation is resumed. + +@end table +@end deftp + +@node Handler actions, Default handler, Condition handling state, General condition handling +@section Handler actions +@cindex Handler actions +@deftp {Data Type} ChfAction + +Enumerated type representing the set of mutually exclusive actions that a +condition handler can request to the condition handling facility. It can +have the following values: + +@table @code + +@item CHF_CONTINUE +Resume the application from the instruction immediately following the +@code{ChfSignal()} call that triggered the invocation of the condition handler. +This action is not allowed if the severity of the current condition is +@code{CHF_FATAL}, since this severity always requires global recovery +to take place. In this case the @code{CHF_CONTINUE} is automatically +changed to @code{CHF_RESIGNAL} when encountered. + +@item CHF_RESIGNAL +Invoke the next handler in the handler stack with the same condition. +This action must be used when the current condition handler was not able +to recover the condition. + +@item CHF_UNWIND +Unwind the application's stack and restore the @code{unwind_context} +associated with the condition handler, performing a non-local control +transfer. @xref{ChfPushHandler()}. + +@c 2.1, cibrario, 18-May-2000 +@c Added descruption of CHF_UNWIND_KEEP +@item CHF_UNWIND_KEEP +Unwind the application's stack and restore the @code{unwind_context} +associated with the condition handler, performing a non-local control +transfer. Unlike @code{CHF_UNWIND}, the current condition group +at the time of the unwind is @strong{not} destroyed, +and can still be accessed after the non-local control transfer has +taken place. +@end table +@end deftp + +@node Default handler, , Handler actions, General condition handling +@section Default handler +@cindex Default handler + +The default condition handler of Chf is automatically +pushed into the condition handler stack by the Chf initialization +functions. It performs the following functions: + +@table @bullet +@item +if called during an unwind, it returns immediately to the caller, +requesting the action @code{CHF_RESIGNAL}, else + +@item +if the severity of the condition being signaled is greater than +@code{CHF_SUCCESS}, it prints the messages associated with the entire +condition group on stderr using the standard function +@code{ChfBuildMessage()} to build the messages. +@strong{Win32: } Since the standard error stream is not available, +the default handler does not print anything out. + +@item +if the severity of the condition being signaled is less than +@code{CHF_FATAL}, it returns to the caller requesting the action +@code{CHF_CONTINUE}, else + +@item +if the @code{CHF_FATAL} condition was @strong{not} signaled during an unwind +operation, it returns to the caller requesting the action +@code{CHF_UNWIND}, otherwise it requests the action @code{CHF_RESIGNAL}. + +@end table + +@c 2.1, cibrario, 18-May-2000 +@c New chapter: Structured condition handling +@node Structured condition handling, Chf conditions, General condition handling, Top +@chapter Structured condition handling +@cindex Structured condition handling + +Structured condition handling places the condition handler associated +with a block of code next to the block itself, instead of into a separate +procedure. Even if it is less flexible and powerful with respect to the +general handling method described before, it is often clearer and +simpler to implement. Chf supports both styles of condition handling; +the structured condition handling is layered above the general one. + +@menu +* Syntax of structured condition handlers:: +* Chf behavior during structured condition handling:: +@end menu + +@node Syntax of structured condition handlers, Chf behavior during structured condition handling, Structured condition handling, Structured condition handling +@section Syntax of structured condition handlers +@cindex Syntax of structured condition handlers + +The syntax establishing a structured condition handler is as follows: + +@example +ChfTry + @{ + ... body ... + @} +ChfCatch + @{ + const ChfDescriptor *exc = ChfGetTopCondition(); + ... condition handler ... + @} +ChfEndTry; +@end example + +The macros @code{ChfTry}, @code{ChfCatch} and @code{ChfEndTry} +expand as follows: + +@example +<< +@{ + sigjmp_buf _chf_sigjmp_buf; + if(sigsetjmp(_chf_sigjmp_buf, 1) == 0) + @{ + ChfPushHandler(CHF_NULL_HANDLER, _chf_sigjmp_buf, CHF_NULL_POINTER); +>> + @{ + ... body ... + @} +<< + ChfPopHandler(); + @} + else + @{ +>> + @{ + const ChfDescriptor *exc = ChfGetTopCondition(); + ... condition handler ... + @} +<< + ChfDiscard(); + @} +@} +>> +@end example + +Above, @code{<<} and @code{>>} mark the starting and ending point of +each macro expansion, respectively. + +The @code{body} is either a statement or a block of statements (a block +is shown in the example) to be protected. If an exception with severity +equal to @code{CHF_FATAL} is signaled during the execution of +@code{body}, control is transferred to the @code{condition handler} +following @code{ChfCatch}. + +The handler can then retrieve the condition group associated with the +exception using @code{ChfGetTopCondition()}, as shown in the example, +and must perform one of the following actions: + +@itemize @bullet +@item +Handle the exception; when the condition handler completes, that is, its +last statement has been executed, the current condition group is +automatically discarded and control is transferred to the code that +follows @code{ChfEndTry}. +@item +Resignal the exception, possibly after adding one or more conditions to +it; in this case, the handler explicitly calls @code{ChfSignal()}. +In response, Chf continues to search for a handler that actually +handles the exception, in the @code{ChfTry}/@code{ChfCatch} blocks +enclosing the current one, in inner to outer order. The control +will never be returned to the resignaling handler. +@end itemize + +Notice that it is not possible for the condition handler to resume +the thread of execution disrupted by the exception. Resuming is +possible only when the signaled condition has severity less than +@code{CHF_FATAL}: in this case the @code{condition handler} is +not invoked, the usual condition signaling sequence takes place +and the @code{CHF_CONTINUE} action requested by any general +condition handler is honored. + +Thus, structured condition handling is further simplified because: + +@itemize @bullet +@item +Conditions with severity less than @code{CHF_WARNING} are usually +automatically signaled immediately after generation and are not reported +to the caller; they are informational conditions only, and they must not +disrupt the normal control flow of the application. Accordingly, the +structured condition handler is @strong{not} invoked when a +success/informational condition is signaled in its @code{ChfTry} body. +@item +@code{CHF_WARNING} conditions generated by a module are usually not +signaled immediately after generation, but are reported to the +caller using some status reporting mechanism. When a warning +condition is generated by a module, it indicates that its execution +has been at least partially successful; this usually means that +the disruption of the control flow of the application can be kept to +a minimum and handled locally. Accordingly, the structured condition +handler is @strong{not} invoked when a warning condition +is signaled in its @code{ChfTry} body. +@item +@code{CHF_ERROR} conditions generated by a module indicate that the +module execution has been unsuccessful; they are usually reported to the +caller using some status reporting mechanisms. An error condition +indicates that a significant disruption of the control flow of the +application is required to recover, and that local handling is still +possible but could not suffice. Accordingly, the structured condition +handler is @strong{not} invoked when an error condition is signaled in +its @code{ChfTry} body. Notice also that if the @code{ChfTry} body is +not able to locally recover the condition, it can add a @code{CHF_FATAL} +condition to the condition group and signal the modified group, thus +triggering the invocation of the structured condition handler. +@item +@code{CHF_FATAL} conditions are usually signaled immediately after +generation and always require a global recovery action. +Accordingly, the structured condition handler is +invoked when a warning condition is signaled in its @code{ChfTry} body. +@end itemize + +Notice also that control is transferred to the structured exception +handler by means of a @code{siglongjmp()} sequence. Therefore, +if the structured exception handler needs to access automatic +variables, they should be declared @code{volatile}. + +If no handlers are able to handle an exception, the default Chf +condition handler is invoked last. @xref{Default handler}, for a more +detailed description of its operation. + +If no exceptions are raised during the execution of @code{body}, +the @code{condition handler} is skipped and execution continues +immediately after @code{ChfEndTry}. + +@node Chf behavior during structured condition handling, , Syntax of structured condition handlers, Structured condition handling +@section Chf behavior during structured condition handling +@cindex Chf behavior during structured condition handling + +When a condition is signaled and the special structured condition +handling helper is invoked, the following sequence of events occurs: + +@itemize @bullet +@item +Chf transitions to state @code{CHF_SIGNALING} and starts +the handler calling sequence. +@item +If any handler invoked before the structured condition handling helper +requests either the @code{CHF_CONTINUE} or the @code{CHF_UNWIND} +action, that action is executed as usual. +@item +When invoked, the structured condition handling helper immediately +requests the action @code{CHF_UNWIND_KEEP}. +@item +Chf transitions to state @code{CHF_UNWINDING} and invokes all handlers +registered after the structured condition handling helper again. Such +handlers can then perform whatever final cleanup operations they need. +@item +Chf invokes the structured condition handling helper again; the helper +immediately returns without doing anything, since Chf is now in state +@code{CHF_UNWINDING}. +@item +Chf transitions to the @code{CHF_IDLE} state and performs a non-local +control transfer to the structured condition handler's unwind context; +this corresponds to the code that follows @code{ChfCatch}. +The current condition group is not removed from the condition stack. +@item +Since the current condition group has not been removed from the +condition stack, the code that follows @code{ChfCatch} can freely +access the condition group using @code{ChfGetTopCondition()} +and @code{ChfGetNextDescriptor()}. +@item +If any new condition is generated in this phase, it is merged with the +current condition group; the condition group can be re-signaled at will +by invoking @code{ChfSignal()}. +@item +Immediately after the code that follows @code{ChfCatch} completes, +@code{ChfDiscard()} is automatically invoked to remove the topmost +condition group from the stack. +@end itemize + +If the structured condition handling helper is invoked when Chf is in +@code{CHF_SIGNAL_UNWINDING}, it immediately requests the +@code{CHF_RESIGNAL} action, because a further unwind request is neither +allowed nor useful in this case. + +The structured condition handling helper makes no use of +@code{handler_context}. + +In summary, the following structured condition handling macros +are currently available: + +@menu +* ChfTry:: +* ChfCatch:: +* ChfEndTry:: +@end menu + +@node ChfTry, ChfCatch, Chf behavior during structured condition handling, Chf behavior during structured condition handling +@subsection ChfTry +@deftypefn Macro {} ChfTry {...} + +This macro introduces a new structured condition handling body. +The body is either a statement or a block of statements to be +protected: if a @code{CHF_FATAL} exception is signaled during its +execution, control is transferred to the condition handling code +following @code{ChfCatch}. + +@end deftypefn + +@node ChfCatch, ChfEndTry, ChfTry, Chf behavior during structured condition handling +@subsection ChfCatch +@deftypefn Macro {} ChfCatch {...} + +This macro must follow @code{ChfTry} and introduces a new structured +condition handler following it; the handler can be either a statement or +a block of statements. If a @code{CHF_FATAL} exception is signaled +during the execution of the body protected by @code{ChfTry}, control is +transferred to the condition handling code following @code{ChfCatch}. + +@end deftypefn + +@node ChfEndTry, , ChfCatch, Chf behavior during structured condition handling +@subsection ChfEndTry +@deftypefn Macro {} ChfEndTry + +This macro marks the end of a body/condition handler pair. + +@end deftypefn + + +@node Chf conditions, Message retrieval, Structured condition handling, Top +@chapter Chf conditions +@cindex Chf conditions + +The Chf library can itself generate and signal the following conditions. +All conditions are fatal, i.e. require a global recovery action to take +place to be handled. + +@deftypefn Macro {} CHF_F_COND_STACK_FULL +The user attempted to generate a condition while the condition stack was full; +this condition is generated anyway using a reserved stack entry. +No additional conditions can be generated until the handling of this condition +is completed, otherwise the application will be aborted. +@end deftypefn + +@deftypefn Macro {} CHF_F_HDLR_STACK_FULL +The function @code{ChfPushHandler()} was called while the handler stack +was full; the new handler is not registered and this condition is signaled +using the old handler hierarchy. +@end deftypefn + +@deftypefn Macro {} CHF_F_HDLR_STACK_EMPTY +A function requiring at least one condition handler to be active +found an empty handler stack. +@end deftypefn + +@deftypefn Macro {} CHF_F_BAD_STATE +The current state of Chf was not appropriate to invoke a certain +Chf function. The invoked function does not do anything but +signaling this condition. +@end deftypefn + +@deftypefn Macro {} CHF_F_INVALID_ACTION +During the current signaling sequence an handler returned an invalid +action code. A new condition group, containing only this condition, is +generated and immediately signaled. The first handler called to handle +the new condition group will be the handler registered immediately before +the faulting one. +@end deftypefn + +@deftypefn Macro {} CHF_F_MALLOC +Many Chf functions dynamically allocate memory. This condition is generated +and signaled when a dynamic memory allocation attempt failed, and the library +was unable to recover in another way. +@end deftypefn + +@deftypefn Macro {} CHF_F_NOT_AVAILABLE +The function is not available on the current platform. This condition +is returned by some Chf initialization functions instead of being +signaled, because it prevents the Chf subsystem to be initialized and, +therefore, to function properly. +@end deftypefn + +@deftypefn Macro {} CHF_F_SETLOCALE +The @code{setlocale()} function failed during the initialization of the +message catalog-based message retrieval subsystem. This condition is returned +by the Chf initialization function instead of being signaled, because it +prevents the Chf subsystem to be initialized and, therefore, to function +properly. +@end deftypefn + +@deftypefn Macro {} CHF_F_CATOPEN +The @code{catopen()} function failed during the initialization of the +message catalog-based message retrieval subsystem. This condition is returned +by the Chf initialization function instead of being signaled, because it +prevents the Chf subsystem to be initialized and, therefore, to function +properly. +@end deftypefn + + +@node Message retrieval, Multithreading support, Chf conditions, Top +@chapter Message retrieval +@cindex Message retrieval +The message retrieval subsystem is responsible to retrieve the message +fragments needed by the Chf library to generate the messages associated +with condition codes. + +Basically, given a message set identifier (that usually has the same value +as the module identifier for which the condition is being generated) and +a message number (that usually has the same value as the condition code), +it must retrieve and return to the caller a suitable message associated +to it. It the message cannot be found, it must return a default message +pointer Chf provides. + +Some module identifiers/message sets are used for special purposes. +@xref{Reserved module identifiers}. + +@menu +* Retrieving a message:: +* Reserved module identifiers:: +* Defining a new message retrieval subsystem:: +@end menu + +@node Retrieving a message, Reserved module identifiers, Message retrieval, Message retrieval +@section Retrieving a message +The following functions directly interact with the message retrieval subsystem: + +@menu +* ChfGetMessage():: +* ChfBuildMessage():: +@end menu + +@node ChfGetMessage(), ChfBuildMessage(), Retrieving a message, Retrieving a message +@subsection ChfGetMessage() +@deftypefn Function {const char *} ChfGetMessage (const int module_id, const int condition_code, const char *default_message) + +This function transparently uses the Chf message retrieval subsystem to +retrieve the message associated with the pair +(@code{module_id}, @code{condition_code}) and returns a pointer to it. +The function will return @code{default_message} instead, if it was not able +to retrieve the message. If @code{module_id==CHF_ERRNO_SET}, the function +will additionally invoke @code{strerror()}, if necessary, to retrieve the +message. + +@strong{Win32:} Since @code{strerror()} is not supported, the additional +last-chance translation described above cannot be performed. + +Notes: + +@itemize @bullet + +@item +The returned pointer points to per-thread static storage, which can be +overwritten by subsequent calls to this function. + +@end itemize +@end deftypefn + + +@node ChfBuildMessage(), , ChfGetMessage(), Retrieving a message +@subsection ChfBuildMessage() +@deftypefn Function {char *} ChfBuildMessage (const ChfDescriptor *descriptor) + +This function builds the message associated with the given condition +descriptor and returns a pointer to a string containing it. + +The message has a standard format and provides the following information +items: + +@itemize @bullet +@item +The application's name given to the Chf library during initialization, if +the condition is the topmost within a condition group; otherwise, the message +starts with an horizontal tab character. +@item +The name of the module that generated the condition. +@item +If available, the file name and line number of the source where the condition +was generated. +@item +The severity code of the message. +@item +The message associated with the condition code, including any ancillary +information provided during condition generation. +@end itemize + +Notes: + +@itemize @bullet + +@item +This function will call @code{ChfAbort()} with abort code @code{CHF_ABORT_INIT} +if Chf hasn't been correctly initialized. + +@item +The returned pointer points to per-thread static storage, which can be +overwritten by subsequent calls to this function. + +@item +@strong{Win32:} to save space, the application's name and severity code +are not included in the message. + +@end itemize +@end deftypefn + +@node Reserved module identifiers, Defining a new message retrieval subsystem, Retrieving a message, Message retrieval +@section Reserved module identifiers +@cindex Reserved module identifiers + +The following module identifiers are currently used by the Chf library; +all module identifiers with value less than 10 are reserved for future use. + +@table @code +@item 1 +This module identifier is used as a message set identifier to retrieve the +names of the other application's modules. In particular, to retrieve the +name of the application's module @code{m}, the message @code{m} of +message set @code{1} is retrieved. + +@item 2 +This module identifier is used to identify the conditions generated by +the Chf library itself. + +@item 3 +This module identifier is used as a message set identifier to retrieve the +messages associated with the @code{errno} error codes. If the retrieve is +unsuccessful, @code{strerror()} is used instead. +@end table + +@strong{Win32:} In addition, the current implementation of the +message retrieval subsystem on Win32 imposes the following limits: +@code{0 <= condition_code <= 999}, @code{0 <= module_id <= 64}. + + +@c 2.1, cibrario, 18-May-2000 +@c Completed section on defining a new mrs +@node Defining a new message retrieval subsystem, , Reserved module identifiers, Message retrieval +@section Defining a new message retrieval subsystem +@cindex Defining a new message retrieval subsystem + +The Chf library has a generic interface towards the message retrieval +subsystem (MRS); therefore, any subsystem can be used, provided it follows +the guidelines described below: + +@itemize @bullet +@item +A copy of the @code{mrs_data} argument of @code{ChfInit()} is passed to +each MRS function when it is invoked. It can be used to hold a pointer +to a private MRS data structures. +@item +The @code{mrs_get} function has the following arguments: + @itemize @bullet + @item + A copy of the @code{mrs_data} argument of @code{ChfInit()}. + @item + An @code{int}, representing the module identifier of the condition message + template/fragment to be retrieved. + @item + An @code{int}, representing the condition code of the condition message + template/fragment to be retrieved. + @item + A @code{char *}, pointing to a default message template/fragment to + be returned if the message retrieval fails. + @end itemize +The function must attempt to retrieve the message template/fragment +associated with the pair (module identifier, condition code) and return +it to the caller; if the retrieval fails, the default message must be +returned instead. +@item +The @code{mrs_exit} function is invoked when Chf is about to exit. +It has as argument a copy of the @code{mrs_data} argument or +@code{ChfInit()} and must perform any cleanup operation the MRS +needs. It is guaranteed that no other MRS function will be called +after a successful invocation of @code{mrs_exit}. +@item +When multithreading support is enabled, the MRS must support multiple, +concurrent invocations made by different application's threads. The same +value of @code{mrs_data} is common to all threads and cannot be changed +after initialization. +@end itemize + +@c 2.1, cibrario, 18-May-2000 +@c New chapter on multithreading support; revised all other chapters to +@c take multithreading support into account, too. +@node Multithreading support, Other useful macro definitions, Message retrieval, Top +@chapter Multithreading support +@cindex Multithreading support + +When compiled appropriately, the Chf library supports multithreading, +and maintains one Chf context per active thread. Multithreading support +is enabled when the cpp macro @code{_REENTRANT} is defined. + +@strong{Win32:} Multitreading support is not provided yet. + +When multithreading support is enabled, the following modifications +to the default Chf behavior come into effect: + +@itemize @bullet +@item +@code{ChfInit()} no longer initializes the condition stack, the +condition handler stack and the message buffer immediately; instead, one +copy of these context elements will be allocated for each active thread +in the application, when it first invokes any other Chf function. +Also, per-thread Chf contexts are dynamically destroyed when the +owning thread terminates. +@item +Condition generation and both general and structured condition handling +work as before inside each application's thread. All threads share the +same values for @code{condition_stack_size}, @code{handler_stack_size} +and @code{exit_code}, but are otherwise fully independent with +respect to condition handling, because each of them has a private +Chf context. +@item +@code{ChfAbort()} no longer exits the whole application when +the @code{CHF_ABORT} Chf flag not set; instead, it exits the +invoking thread only. +@item +Almost all Chf functions can trigger the @code{CHF_ABORT_GET_CONTEXT} +and @code{CHF_ABORT_PTHREAD} aborts, if they fail to correctly +manage dynamic Chf context creation and/or @code{pthread} interactions. +@item +All Chf functions, except @code{ChfInit()} and @code{ChfExit()} +no longer check whether Chf has been correctly initialized or not: +that check would have been too time-consuming in a multithreaded environment. +As a consequence, invoking a Chf function when Chf has not been correctly +initialized has unpredictable effects instead of neatly aborting +the application with abort code @code{CHF_ABORT_INIT}. +@item +The performance of the Chf condition generation and signaling functions +is likely to decrease, because Chf must retrieve its per-thread context +dynamically instead of referring to a static storage area. +@end itemize + +@node Other useful macro definitions, Other useful functions, Multithreading support, Top +@chapter Other useful macro definitions +@cindex Other useful macro definitions + +@deftypefn Macro {} CHF_MAX_MESSAGE_LENGTH +Represents the maximum allowable length for condition messages. +@end deftypefn + +@deftypefn Macro {} CHF_LIBRARY_ID +It is a string representing the Chf library identifier; the identifier +contains also the library release number. +@end deftypefn + +@c 2.1, cibrario, 18-May-2000 +@c New macros +@deftypefn Macro {} CHF_MAJOR_RELEASE_NUMBER +It is an integer representing the Chf library major release number. +@end deftypefn + +@deftypefn Macro {} CHF_MINOR_RELEASE_NUMBER +It is an integer representing the Chf library minor release number. +@end deftypefn + +@deftypefn Macro {} CHF_MODULE_NAMES_SET +Contains the integer identifier of the message set whose messages are used to +retrieve the names of the application's modules. It has the value 1. +@end deftypefn + +@deftypefn Macro {} CHF_SET +Contains the integer identifier of the message set containing the condition +messages Chf uses internally. It has the value 2. +@end deftypefn + +@deftypefn Macro {} CHF_ERRNO_SET +Contains the integer identifier of the message set containing the condition +messages for @code{errno} codes. It has the value 3. +@end deftypefn + +@deftypefn Macro {} ChfChar +This macro is used to make portable declarations of character variables, +and is mapped into either @code{char} or @code{TCHAR} depending on the +platform. +@end deftypefn + +@deftypefn Macro {} ChfText (x) +This macro is used to make portable declarations of character strings, +and is mapped into either @code{x} or @code{_T(x)} depending on the +platform. +@end deftypefn + +@deftypefn Macro {} ChfSigjmp_buf +This macro is mapped into either @code{sigjmp_buf} or @code{jmp_buf}, +depending on whether the platform supports @code{sigjmp_buf} +contexts or not. +@end deftypefn + +@deftypefn Macro {} ChfSigsetjmp (x,y) +This macro is mapped into either @code{sigsetjmp()} or @code{setjmp()}, +depending on whether the platform supports @code{sigjmp_buf} +contexts or not. In the latter case argument @code{x} is ignored. +@end deftypefn + +@deftypefn Macro {} ChfSiglongjmp (x,y) +This macro is mapped into either @code{siglongjmp()} or @code{longjmp()}, +depending on whether the platform supports @code{sigjmp_buf} +contexts or not. +@end deftypefn + +@node Other useful functions, Changes since release 1, Other useful macro definitions, Top +@chapter Other useful functions +@cindex Other useful functions + +@deftypefn Function void ChfDiscard (void) +This function discards the topmost condition group currently in the +condition stack, without signaling it. The function does nothing if +the condition stack is empty. + +This function uses the function @code{ChfAbort()} to +abort the application if Chf has not been initialized correctly +(abort code @code{CHF_ABORT_INIT}). +@end deftypefn + +@deftypefn Function {const ChfDescriptor *} ChfGetTopCondition (void) +This function returns to the caller a pointer to the topmost condition of +the current condition group. It generates and immediately signals the +condition @code{CHF_F_BAD_STATE} if the current condition group is empty. + +This function can be useful to retrieve the complete description of a +condition for functions that, when an error occurs, generate a condition +and return to the caller a generic error indication, such as a distinguished +return value. + +The same function can also be useful in a structured exception handler, +to retrieve the condition descriptor associated with the exception to +be handled. + +Notes: + +@itemize @bullet +@item +During condition signaling, Chf creates a new, empty, condition group +immediately before starting the invocation sequence of the condition +handlers, as previously described. Therefore +@code{ChfGetTopCondition()}, when called from a condition handler, will return +a pointer to the top condition generated during the handling ONLY, if any, and +NOT to the top condition of the condition group being signaled. The +latter pointer is directly available, as an argument, to condition +handlers. +@item +When called from a structured exception handler, @code{ChfGetTopCondition()} +works as expected, that is, it returns a pointer to the top condition of +the condition group associated with the exception being handled. +@item +This function will call @code{ChfAbort()} with abort code @code{CHF_ABORT_INIT} +if Chf hasn't been correctly initialized. +@item +The returned pointer is no longer valid when any other Chf function +is called after @code{ChfGetTopCondition()}. +@end itemize +@end deftypefn + +@c 2.1, cibrario, 18-May-2000 +@c New chapter on changes since release 1 +@node Changes since release 1, Changes since release 2.1, Other useful functions, Top +@chapter Changes since release 1 +@cindex Changes since release 1 + +@itemize @bullet +@item +Chf now supports structured condition handling. +@xref{Structured condition handling}. +@item +Chf now supports multithreaded applications; each thread has its own Chf +context. Multithreading support is conditionally enabled by linking a +multithreaded version of the Chf library. +@xref{Multithreading support}. +@item +Chf now supports the conditional save and restore of signal masks in unwind +contexts (@code{sigjmp_buf} contexts instead of @code{jmp_buf}). +@item +The method of passing unwind contexts as arguments is now consistent +with that used by @code{sigsetjmp()}; in particular, it is no longer +necessary to reference the contexts with @code{&}. +@item +Some more useful macro definitions have been added to @code{Chf.h}. +@xref{Other useful macro definitions}. +@end itemize + +@c 2.2, cibrario, 25-Jan-2001 +@c New chapter on changes since release 2.1 +@node Changes since release 2.1, Function Index, Changes since release 1, Top +@chapter Changes since release 2.1 +@cindex Changes since release 2.1 + +@itemize @bullet +@item +Chf now offers limited support of Unicode-based Win32 platforms, +notably Windows CE. +@end itemize + +@node Function Index, Data Type Index, Changes since release 2.1, Top +@unnumbered Function Index +@printindex fn + +@node Data Type Index, Concept Index, Function Index, Top +@unnumbered Data Type Index +@printindex tp + +@c @node Variable Index, Concept Index, Data Type Index, Top +@c @unnumbered Variable Index +@c @printindex vr + +@node Concept Index, , Data Type Index, Top +@unnumbered Concept Index +@printindex cp + +@contents + +@bye diff --git a/Chf/chf_abrt.c b/Chf/chf_abrt.c new file mode 100644 index 0000000..ce1993c --- /dev/null +++ b/Chf/chf_abrt.c @@ -0,0 +1,199 @@ +/* .+ + +.identifier : $Id: chf_abrt.c,v 2.2 2001/01/25 12:08:24 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_abrt.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the CHF function ChfAbort() + +.include : Chf.h + +.notes : + $Log: chf_abrt.c,v $ + Revision 2.2 2001/01/25 12:08:24 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 14:22:07 cibrario + - Conditional inclusion of pthread.h (mt support) + - Expanded abort message table with mt support messages + - ChfAbort() with CHF_ABORT flag clear and mt support enabled now + exits invoking thread only + + Revision 1.1 1996/05/28 12:53:26 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_abrt.c,v 2.2 2001/01/25 12:08:24 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + +#ifdef _REENTRANT +#include +#endif + + +/* Abort codes message table; the relative position of the messages must + match the numeric codes CHF_ABORT_xxxx defined in ChfPriv.h +*/ +static const ChfChar *message_table[] = +{ + (const ChfChar *)NULL, + ChfText("Not initialized"), + ChfText("Temporary message buffer overflow"), + ChfText("Invalid action from last chance handler"), + ChfText("Already initialized"), + ChfText("Unwind request while unwinding"), + ChfText("Improperly handled condition"), + ChfText("Fatal condition while unwinding"), + ChfText("Condition stack overflow"), + ChfText("Can't prime a new Chf context"), + ChfText("Pthread interaction failed") +}; + +#define MESSAGE_TABLE_SIZE (sizeof(message_table)/sizeof(const ChfChar *)) + + +/* .+ + +.title : ChfAbort +.kind : C function +.creation : 13-May-1996 +.description : + This function prints the message associated with 'abort_code' and then + immediately aborts either the application (when multithreading support not + enabled or CHF_ABORT set) or the invoking thread only (multithreading + support enabled and CHF_ABORT not set). The abort is performed either: + - using abort() if either CHF has not been correctly initialized or + the chf_context.options flag CHF_ABORT is set + - using exit(chf_context.exit_code) (multithreading support not enabled) + or pthread_exit(chf_context.exit_code) (multithreading support enabled) + if the flag is clear + + No message is printed if the abort code is CHF_ABORT_SILENT; this code + is used, for example, by the default condition handler to terminate the + application when a CHF_FATAL condition occours. + + NOTE: This function must be called only when either a serious internal CHF + failure occurs or it's necessary to abort the application. + + WIN32: + + - stderr stream is not supported; the abort message is displayed in a + message box only if Chf has been correctly initialized, otherwise the + abort will be done silently + + - abort() is not supported and has been replaced by exit(EXIT_FAILURE) + +.call : + ChfAbort(abort_code); +.input : + const int abort_code, abort_code +.output : + void +.status_codes : + none +.notes : + 1.1, 13-May-1996, creation + 2.1, 19-May-2000, update: + - added multithreading support + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +void ChfAbort( /* Abort application */ + const int abort_code +) +{ +#ifdef _WIN32 + if(abort_code != CHF_ABORT_SILENT) + { + TCHAR abort_msg[CHF_MAX_MESSAGE_LENGTH]; + HWND active_window; + + /* stderr not available; + put complaint in a message box and display it + */ + if(abort_code < 0 || abort_code >= MESSAGE_TABLE_SIZE) + _stprintf(abort_msg, + CHF_ABORT_BAD_CODE_FMT, abort_code); + + else + _stprintf(abort_msg, + CHF_ABORT_GOOD_CODE_FMT, message_table[abort_code]); + + /* Return value of MessageBox() ignored, because there is only + one available choice (abort) here. Avoid using a NULL handle. + */ + if(chf_context.state != CHF_UNKNOWN + && (active_window = GetActiveWindow()) != (HWND)NULL) + (void) + MessageBox(active_window, + abort_msg, + chf_context.app_name, + MB_OK + |MB_ICONERROR + |MB_APPLMODAL|MB_SETFOREGROUND); + } + + /* Immediately exit the application with exit code EXIT_FAILURE + if CHF_ABORT option is set or if something is wrong with Chf state. + */ + if(chf_context.state == CHF_UNKNOWN || chf_context.options & CHF_ABORT) + exit(EXIT_FAILURE); + + else + /* Else, exit the application anyway, but with the exit code + registered by the application. Don't use PostQuitMessage(), + because the contract is that ChfAbort() never returns to the caller. + */ +#ifndef _REENTRANT + exit(chf_context.exit_code); +#else +#error "_REENTRANT not supported yet" +#endif + +#else + if(abort_code != CHF_ABORT_SILENT) + { + fputs(CHF_ABORT_HEADER, stderr); + + if(abort_code < 0 || abort_code >= MESSAGE_TABLE_SIZE) + fprintf(stderr, CHF_ABORT_BAD_CODE_FMT, abort_code); + + else + fprintf(stderr, CHF_ABORT_GOOD_CODE_FMT, message_table[abort_code]); + } + + if(chf_context.state == CHF_UNKNOWN || chf_context.options & CHF_ABORT) + abort(); + + else +#ifndef _REENTRANT + exit(chf_context.exit_code); +#else + pthread_exit((void *)(chf_context.exit_code)); +#endif +#endif +} diff --git a/Chf/chf_gen.c b/Chf/chf_gen.c new file mode 100644 index 0000000..7b389d0 --- /dev/null +++ b/Chf/chf_gen.c @@ -0,0 +1,179 @@ +/* .+ + +.identifier : $Id: chf_gen.c,v 2.2 2001/01/25 12:10:22 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_gen.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module contains the condition generation function of CHF + +.include : Chf.h + +.notes : + $Log: chf_gen.c,v $ + Revision 2.2 2001/01/25 12:10:22 cibrario + Added partial Win32 support (Windows CE only). + + Revision 1.1 1996/05/28 12:53:59 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_gen.c,v 2.2 2001/01/25 12:10:22 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* .+ + +.title : ChfGenerate +.kind : C function +.creation : 3-May-1996 +.description : + This function generates a condition descriptor for the condition described + by 'module_id', 'file_name', 'line_number', 'condition_code' and 'severity', + and the partial message associated with it, specialized with the additional + arguments '...'; the new condition descriptor is put onto the top of the + condition stack. + + If the condition stack is full, this function generates the condition + CHF_F_COND_STACK_FULL and immediately invokes ChfSignal() for the current + condition stack. + + NOTE: This function calls the CHF function 'ChfAbort()' to + abort the application if either: + - CHF has not been initialized correctly (abort code CHF_ABORT_INIT) + - there is an overflow in the internal buffer used during the + generation of the partial message associated with the condition + (abort code CHF_ABORT_MSG_OVF) + - there was an attempt to generate a condition while the CHF condition + CHF_F_COND_STACK_FULL (condition stack full) was being signalled + (abort code CHF_ABORT_COND_STACK_OVF) + + +.call : + ChfGenerate(module_id, file_name, line_number, + condition_code, severity, ...); +.input : + const int module_id, module identifier + const char *file_name, file name + const int line_number, line number + const int condition_code, condition code + const ChfSeverity severity, severity + ..., additional arguments +.output : + void +.status_codes : + (*) CHF_F_COND_STACK_FULL, the condition stack is full +.notes : + 1.1, 3-May-1996, creation + +.- */ +void ChfGenerate( /* Generate a condition into the stack */ + const int module_id, + const ChfChar *file_name, + const int line_number, + const int condition_code, + const ChfSeverity severity, + ... +) +{ + ChfDescriptor *new_descriptor; + va_list aux_arg; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + /* Prepare the additional arguments list */ + va_start(aux_arg, severity); + + if((new_descriptor = chf_context.condition_sp) - + chf_context.condition_stack >= chf_context.condition_stack_size) + { + /* The condition stack is full; + generate the CHF_F_COND_STACK_FULL condition and signal it immediately, + using the last available slot of the stack, if it's still empty, + otherwise abort the application. + */ + if(new_descriptor - chf_context.condition_stack == + chf_context.condition_stack_size) + { + new_descriptor->module_id = CHF_MODULE_ID; + new_descriptor->condition_code = CHF_F_COND_STACK_FULL; + new_descriptor->severity = CHF_FATAL; + new_descriptor->line_number = CHF_UNKNOWN_LINE_NUMBER; + new_descriptor->file_name = CHF_UNKNOWN_FILE_NAME; + + ChfStrncpy(new_descriptor->message, + ChfGetMessage(CHF_MODULE_ID, CHF_F_COND_STACK_FULL, + ChfText("Condition stack is full")), CHF_MAX_MESSAGE_LENGTH-1); + new_descriptor->message[CHF_MAX_MESSAGE_LENGTH-1] = '\0'; + + new_descriptor->next = CHF_NULL_DESCRIPTOR; + chf_context.condition_sp++; + + ChfSignal(); + } + + else + ChfAbort(CHF_ABORT_COND_STACK_OVF); + } + + else + { + ChfChar def_message[CHF_DEF_MESSAGE_LENGTH]; + ChfChar tmp_message[CHF_TMP_MESSAGE_LENGTH]; + + new_descriptor->module_id = module_id; + new_descriptor->condition_code = condition_code; + new_descriptor->severity = severity; + new_descriptor->line_number = line_number; + new_descriptor->file_name = file_name; + + /* Generate the default message */ + ChfSprintf(def_message, CHF_DEF_PARTIAL_MSG_FMT, condition_code); + + /* Generate the partial message associated with the condition using a + temporary area + */ + if( + ChfVsprintf(tmp_message, + ChfGetMessage(module_id, condition_code, def_message), aux_arg) >= + CHF_TMP_MESSAGE_LENGTH) + ChfAbort(CHF_ABORT_MSG_OVF); + + /* Copy the message into the condition descriptor */ + ChfStrncpy(new_descriptor->message, tmp_message, CHF_MAX_MESSAGE_LENGTH-1); + new_descriptor->message[CHF_MAX_MESSAGE_LENGTH-1] = '\0'; + + /* Link the new descriptor with the current descriptor list, if it + isn't the first descriptor of the list + */ + new_descriptor->next = (new_descriptor == chf_context.condition_base) + ? CHF_NULL_DESCRIPTOR : new_descriptor - 1; + + chf_context.condition_sp++; + } +} diff --git a/Chf/chf_hdlr.c b/Chf/chf_hdlr.c new file mode 100644 index 0000000..e5723b6 --- /dev/null +++ b/Chf/chf_hdlr.c @@ -0,0 +1,264 @@ +/* .+ + +.identifier : $Id: chf_hdlr.c,v 2.2 2001/01/25 12:12:46 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_hdlr.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the CHF functions ChfPushHandler() and + ChfPopHandler() + +.include : Chf.h + +.notes : + $Log: chf_hdlr.c,v $ + Revision 2.2 2001/01/25 12:12:46 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 14:45:04 cibrario + - Implemented StructuredHelper(), the structured condition handling + helper handler + - Updated ChfPushHandler() to push the structured condition handling + helper when new_handler is CHF_NULL_HANDLER + - unwind_context is now a sigjmp_buf, passed as argument directly, + that is, without additional address operators + - improved documentation of ChfPopHandler() + + Revision 1.6 1997/01/15 13:44:39 cibrario + The function ChfPushHandler() has the new argument 'handler_context'. + + Revision 1.1 1996/05/28 12:54:28 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_hdlr.c,v 2.2 2001/01/25 12:12:46 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* .+ + +.title : StructuredHelper +.kind : C function +.creation : 19-May-2000 +.description : + This function is the structured condition handling helper of CHF. + It's automatically pushed into the condition handler stack by + ChfPushHandler() when its 'new_handler' argument is CHF_NULL_HANDLER, + and performs the following functions: + + - if called during an ordinary signalling operation with a + CHF_FATAL condition, it requests the action CHF_UNWIND_KEEP + + - if called when Chf is in any other state, or with a + severity less than CHF_FATAL, it requests the action CHF_RESIGNAL + + The structured condition handling helper currently makes no use of + handler_context. + +.call : + action = StructuredHelper(desc, state, context); +.input : + const ChfDescriptor *desc, condition descriptor + const ChfState state, current CHF state +.output : + ChfAction action, action requested by the handler +.status_codes : + none +.notes : + 2.1, 19-May-2000, creation + +.- */ +static ChfAction StructuredHelper( + const ChfDescriptor *desc, + const ChfState state, + ChfPointer handler_context +) +{ + ChfAction action; + const ChfDescriptor *d; + + return((state == CHF_SIGNALING && ChfGetSeverity(desc) == CHF_FATAL) + ? CHF_UNWIND_KEEP : CHF_RESIGNAL); +} + + +/* .+ + +.title : ChfPushHandler +.kind : C function +.creation : 13-May-1996 +.description : + This function pushes the new condition handler 'new_handler' with its + associated longjmp context pointed by 'unwind_context' into the handler + stack and returns CHF_S_OK to the caller. If 'new_handler' is + CHF_NULL_HANDLER, the special structured condition handling helper + 'StructuredHelper()' is pushed instead. + + Moreover, this function saves a copy of the pointer 'handler_context'; it + will be passed to 'new_handler' upon each subsequent activation, and + therefore can be used as a private handler context pointer. The user must + assure that the information pointed by 'handler_context', if any, will + remain valid until 'new_handler' is popped from the condition stack. + 'handler_context' may be set to the special (null) value CHF_NULL_POINTER to + indicate that the handler hasn't any private context information. + + If, in the future, the handler will request the CHF_UNWIND action, the + setjmp() function invocation that established 'unwind_context' will appear + to return again. + + 'unwind_context' can be the reserved (null) pointer CHF_NULL_CONTEXT; in + this case, if the handler will request the CHF_UNWIND_ACTION, the + application will be silently terminated calling ChfAbort() with abort code + CHF_ABORT_SILENT. + + If some error occours during the execution, the function will generate + and immediately signal one of the conditions listed below and marked with + (*). The function will never return dorectly to the caller, since all + conditions are CHF_FATAL. + + NOTE: This function calls ChfAbort() with abort code CHF_ABORT_INIT if + the CHF subsystem has not been initialized. + +.call : + ChfPushHandler(new_handler, unwind_context); +.input : + ChfHandler new_handler, new condition handler + void *unwind_context, handler unwind context pointer + ChfPointer handler_context, private handler context pointer +.output : + void +.status_codes : + (*) CHF_F_BAD_STATE, bad CHF state for requested operation + (*) CHF_F_HDLR_STACK_FULL, the handler stack is full +.notes : + 1.1, 13-May-1996, creation + 1.6, 15-Jan-1997, update: + - added the argument 'handler_context' + - improved documentation + 2.1, 19-May-2000, update: + - now using sigjmp_buf as unwind_context + - added StructuredHelper handling + +.- */ +void ChfPushHandler( /* Push a new handler into the stack */ + ChfHandler new_handler, + void *unwind_context, + ChfPointer handler_context +) +{ + /* Make sure that CHF has been correctly initialized and is idle */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if(chf_context.state != CHF_IDLE) + { + ChfCondition CHF_F_BAD_STATE, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + /* Check if the handler stack is full */ + else if(chf_context.handler_sp - chf_context.handler_stack >= + chf_context.handler_stack_size) + { + ChfCondition CHF_F_HDLR_STACK_FULL, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + else + { + chf_context.handler_sp->unwind_context = unwind_context; + chf_context.handler_sp->handler_context = handler_context; + chf_context.handler_sp->handler = + ((new_handler == CHF_NULL_HANDLER) ? StructuredHelper : new_handler); + chf_context.handler_sp++; + } +} + + +/* .+ + +.title : ChfPopHandler +.kind : C function +.creation : 13-May-1996 +.description : + This function pops the topmost condition handler from the handler stack and + returns to the caller. + + If some error occours during the execution, the function will generate + and immediately signal one of the conditions listed below and marked with + (*). The function will never return directly to the caller, since all + conditions are CHF_FATAL. + + NOTE: This function calls ChfAbort() with abort code CHF_ABORT_INIT if + the CHF subsystem has not been initialized. + +.call : + ChfPopHandler(); +.input : + void +.output : + void +.status_codes : + CHF_F_BAD_STATE, bad CHF state for requested operation + CHF_F_HDLR_STACK_FULL, the handler stack is full +.notes : + 1.1, 13-May-1996, creation + 1.6, 15-Jan-1997, update: + - improved documentation + 2.1, 19-May-2000, update: + - improved documentation + +.- */ +void ChfPopHandler( /* Pop a handler */ + void +) +{ + /* Make sure that CHF has been correctly initialized and is idle */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if(chf_context.state != CHF_IDLE) + { + ChfCondition CHF_F_BAD_STATE, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + /* Check if the handler stack is empty */ + else if(chf_context.handler_sp == chf_context.handler_stack) + { + ChfCondition CHF_F_HDLR_STACK_EMPTY, CHF_FATAL + ChfEnd; + + ChfSignal(); + } + + /* Discard the topmost condition handler */ + else + --chf_context.handler_sp; +} diff --git a/Chf/chf_init.c b/Chf/chf_init.c new file mode 100644 index 0000000..fa64a68 --- /dev/null +++ b/Chf/chf_init.c @@ -0,0 +1,780 @@ +/* .+ + +.identifier : $Id: chf_init.c,v 2.2 2001/01/25 14:05:23 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_init.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the CHF initialization function ChfInit() + +.include : Chf.h + +.notes : + $Log: chf_init.c,v $ + Revision 2.2 2001/01/25 14:05:23 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 15:30:42 cibrario + - Renamed static context chf_context to _chf_context; it is now in + actual use only when mt support is not enabled, otherwise it is + used only as a prototype to prime the per-thread contexts on demand + - Conditional definition of mt support static variables + (context_mutex to access the static master context, fputs_mutex to + ensure the atomicity of DefaultHandler() while it is printing a + sequence of grouped condition messages) and functions (DestroyContext() + to destroy a per-thread context when the owning thread terminates) + - Deeply revised ChfInit() and ChfExit() to implement multithreading + support + - Implemented function _ChfGetContext() to return a pointer to the + per-thread Chf context, creating them on-demand. + + Revision 1.6 1997/01/15 13:37:19 cibrario + The Chf condition handlers now have a third argument: the private handler + context pointer; the code has been updated accordingly. + + Revision 1.4 1996/09/25 13:29:01 cibrario + Added static char[] variable rcs_lib_id; it contains the value of the + CHF_LIBRARY_ID macro. The header Chf.h sets that macro to its RCS Id, + that is also the Id of the whole Chf library. + + Revision 1.1 1996/05/28 12:54:58 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_init.c,v 2.2 2001/01/25 14:05:23 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* Chf Library Id */ +#ifndef lint +static ChfChar rcs_lib_id[] = CHF_LIBRARY_ID; +#endif + +/* CHF context */ +ChfContext _chf_context; + +/* Message separator and severity names for ChfBuildMessage() */ +static const ChfChar separator[] = CHF_MESSAGE_SEPARATOR; +static const ChfChar *severity_name[] = CHF_SEVERITY_NAMES; + + +/* ------------------------------------------------------------------------- + Multithreading support + ------------------------------------------------------------------------- */ +#ifdef _REENTRANT +#include + +/* Mutex to access chf_context during initialization and exit; + mutex to puts condition messages on stderr (DefaultHandler) +*/ +static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t fputs_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Chf data key */ +static pthread_key_t data_key; + +/* This function is called to destroy a Chf context when the owning + thread terminated. +*/ +static void DestroyContext(void *context) +{ + free(((ChfContext *)context)->message_buffer); + free(((ChfContext *)context)->handler_stack); + free(((ChfContext *)context)->condition_stack); + free(context); +} +#endif + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + + +/* .+ + +.title : DefaultHandler +.kind : C function +.creation : 17-May-1996 +.description : + This function is the default condition handler of CHF. It's automatically + pushed into the condition handler stack by ChfInit() and performs the + following functions: + + - if called during an unwind, it returns immediately to the caller, + requesting the action CHF_RESIGNAL, else + + - if the severity of the condition being signalled is greater than + CHF_SUCCESS, it prints the messages associated with the entire + condition group on stderr using the standard function + ChfBuildMessage() to build the messages. + + - if the severity of the condition being signalled is less than + CHF_FATAL, it returns to the caller requesting the action + CHF_CONTINUE, else + + - if the CHF_FATAL condition was NOT signalled during an unwind + operation, it returns to the caller requesting the action + CHF_UNWIND, otherwise it requests the action CHF_RESIGNAL. + + WIN32: + + - stderr stream is not available and modal MessageBox require the + parent's handle to work reliably; the default handler does not print + anything + +.call : + action = DefaultHandler(desc, state, context); +.input : + const ChfDescriptor *desc, condition descriptor + const ChfState state, current CHF state +.output : + ChfAction action, action requested by the handler +.status_codes : + none +.notes : + 1.1, 16-May-1996, creation + 1.6, 15-Jan-1997, update: + - the Chf condition handlers now have a third argument: the private + handler context pointer. + 2.1, 25-May-2000, update: + - added multithreading support + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +static ChfAction DefaultHandler( + const ChfDescriptor *desc, + const ChfState state, + ChfPointer handler_context +) +{ + ChfAction action; + const ChfDescriptor *d; + + if(state == CHF_UNWINDING) + /* If CHF is unwinding, do nothing */ + action = CHF_RESIGNAL; + + else + { + /* Print the condition messages, if necessary. The sequence of fputs() + is done atomically if multithreading support is enabled. + In Win32, the default handler does not print anything. + */ + if(ChfGetSeverity(desc) > CHF_SUCCESS) + { +#ifdef _REENTRANT + if(pthread_mutex_lock(&fputs_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif +#ifndef _WIN32 + for(d = desc; d != CHF_NULL_DESCRIPTOR; d = ChfGetNextDescriptor(d)) + fputs(ChfBuildMessage(d), stderr); +#endif +#ifdef _REENTRANT + if(pthread_mutex_unlock(&fputs_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif + } + + /* Determine the handler action */ + switch(ChfGetSeverity(desc)) + { + case CHF_SUCCESS: + case CHF_INFO: + case CHF_WARNING: + case CHF_ERROR: + { + /* Continue execution if the severity is less than CHF_FATAL */ + action = CHF_CONTINUE; + break; + } + + default: + { + /* The severity of the condition is CHF_FATAL; appempt to unwind if + the fatal condition wasn't signalled during another unwind. + */ + action = ((state == CHF_SIGNAL_UNWINDING) ? CHF_RESIGNAL: CHF_UNWIND); + break; + } + } + } + + /* Return the action code to the Chf handler dispatcher */ + return action; +} + + + +/* .+ + +.title : scopy +.kind : C function +.creation : 16-May-1996 +.description : + This function writes the NUL-terminated string pointed by 'q' starting + from 'p', including the NUL terminator and without trepassing 'p_end'. + The function returns a pointer to the NUL-terminator just written. + +.call : + np = scopy(p, q, p_end); +.input : + char *p, starting position for the write + const char *q, pointer to the string to be copied + char *p_end, pointer to the end of the output area +.output : + char *np, pointer to the NUL-terminator just written +.status_codes : + none +.notes : + 1.1, 16-May-1996, creation + +.- */ +static ChfChar *scopy( + ChfChar *p, + const ChfChar *q, + ChfChar *p_end +) +{ + size_t q_len = ChfStrlen(q); + size_t p_avail = p_end - p; + + if(q_len < p_avail) + { + ChfStrcpy(p, q); + p += q_len; + } + + else if(p_avail > 1) + { + ChfStrncpy(p, q, p_avail-2); + p[p_avail-1] = '\0'; + p = p_end; + } + + return p; +} + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfGetMessage +.kind : C function +.creation : 17-May-1996 +.description : + This function retrieves the message associated with the pair + ('module_id', 'condition_code') and returns a pointer to it. The function + will return 'default_message' if it isn't able to retrieve the message. + If module_id==CHF_ERRNO_SET, the function will use strerror(), if + necessary, to retrieve the requested message. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been correctly initialized. + + NOTE: The returned pointer points to per-thread static storage, which will be + overwritten by subsequent calls to this function. + + WIN32: + + - strerror() is not supported; the last-chance translation of condition + codes in CHF_ERRNO_SET is not performed + +.call : + message = ChfGetMessage(module_id, condition_code, + default_message); +.input : + const int module_id, module identifier + const int condition_code, condition code + const char *default_message, default message +.output : + const char *message, pointer to the retrieved message +.status_codes : + none +.notes : + 1.1, 17-May-1996, creation + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +const ChfChar *ChfGetMessage( /* Retrieve a condition message */ + const int module_id, + const int condition_code, + const ChfChar *default_message +) +{ + const ChfChar *message; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if((message = chf_context.mrs_get(chf_context.mrs_data, + module_id, condition_code, default_message)) == default_message && + module_id == CHF_ERRNO_SET) +# ifdef _WIN32 + message = default_message; +# else + message = strerror(condition_code); +# endif + + return(message); +} + + +/* .+ + +.title : ChfBuildMessage +.kind : C function +.creation : 16-May-1996 +.description : + This function builds the message associated with the given condition + descriptor and returns a pointer to a string containing it. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been correctly initialized. + + NOTE: The returned pointer points to per-thread static storage, which will be + overwritten by subsequent calls to this function. + + WIN32: + + - to save space, the application's name and severity code are not + included in the message + +.call : + msg = ChfBuildMessage(descriptor); +.input : + const ChfDescriptor *descriptor, condition descriptor +.output : + char *msg, pointer to the message associated with 'descriptor' +.status_codes : + none +.notes : + 1.1, 16-May-1996, creation + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +ChfChar *ChfBuildMessage( /* Build a condition message */ + const ChfDescriptor *descriptor +) +{ + ChfChar *tmp_p; + ChfChar *tmp_end; + ChfChar def_message[CHF_DEF_MESSAGE_LENGTH]; + ChfSeverity severity; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + /* Set appropriate pointers to the start/end of the message buffer */ + tmp_p = chf_context.message_buffer; + tmp_end = tmp_p + CHF_MAX_MESSAGE_LENGTH; + +#ifndef _WIN32 + /* The message starts with ": " if the condition is the first of + its condition group, with "\t" if not. + */ + if(descriptor == chf_context.condition_sp-1) + { + tmp_p = scopy(tmp_p, chf_context.app_name, tmp_end); + tmp_p = scopy(tmp_p, separator, tmp_end); + } + + else + tmp_p = scopy(tmp_p, ChfText("\t"), tmp_end); +#endif + + /* The message continues with the module name */ + ChfSprintf(def_message, CHF_DEF_MID_MSG_FMT, ChfGetModuleId(descriptor)); + + tmp_p = scopy(tmp_p, + ChfGetMessage(CHF_MODULE_NAMES_SET, ChfGetModuleId(descriptor), + def_message), tmp_end); + + /* Add also the extended information, if any */ + if(ChfGetLineNumber(descriptor) != CHF_UNKNOWN_LINE_NUMBER) + { + tmp_p = scopy(tmp_p, ChfText(" "), tmp_end); + + ChfSprintf(def_message, CHF_EXTENDED_INFO_FMT, + ChfGetFileName(descriptor), ChfGetLineNumber(descriptor)); + + tmp_p = scopy(tmp_p, def_message, tmp_end); + } + + tmp_p = scopy(tmp_p, separator, tmp_end); + +#ifndef _WIN32 + /* Add the severity code of the message */ + tmp_p = scopy(tmp_p, + ((severity = ChfGetSeverity(descriptor)) < CHF_SUCCESS || + severity > CHF_FATAL) ? CHF_UNKNOWN_SEVERITY : severity_name[severity], + tmp_end); + + tmp_p = scopy(tmp_p, separator, tmp_end); +#endif + + /* The message ends with the partial message from the descriptor */ + tmp_p = scopy(tmp_p, ChfGetPartialMessage(descriptor), tmp_end); + (void)scopy(tmp_p, CHF_MESSAGE_TERMINATOR, tmp_end); + + return chf_context.message_buffer; +} + + +/* .+ + +.title : ChfInit +.kind : C function +.creation : 13-May-1996 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully ChfInit() before using any other CHF + function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_PTHREAD + if a pthread operation fails. + +.call : + cc = ChfInit(app_name, options, + mrs_data, mrs_get, mrs_exit, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const char *app_name, Application's name + const ChfOptions options, Options + void *mrs_data, Message retrieval private data + ChfMrsGet mrs_get, 'GetMessage' function + ChfMrsExit mrs_exit, 'Exit' function + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_MALLOC, FATAL, dynamic memory allocation failed +.notes : + 1.1, 13-May-1996, creation + 1.6, 15-Jan-1997, update: + - updated the call to ChfPushHandler() to accomodate its new interface. + 2.1, 19-May-2000, update: + - added multithreading support + 2.2, 22-Jan-2001, update: + - added Win32 support; a malloc() call was not portable. + +.- */ +int ChfInit( /* Generic initialization */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + void *mrs_data, /* Message retrieval private data */ + ChfMrsGet mrs_get, /* 'GetMessage' function */ + ChfMrsExit mrs_exit, /* 'Exit' function */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ + int cc; + + /* Check that CHF has not been initialized yet */ +#ifndef _REENTRANT + if(_chf_context.state != CHF_UNKNOWN) ChfAbort(CHF_ABORT_DUP_INIT); +#else + /* Reentrant check; lock context_mutex first */ + if(pthread_mutex_lock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + if(_chf_context.state != CHF_UNKNOWN) + { + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + ChfAbort(CHF_ABORT_DUP_INIT); + } +#endif + +#ifndef _REENTRANT + if((_chf_context.condition_stack = + (ChfDescriptor *) + malloc((size_t)(condition_stack_size+1)*sizeof(ChfDescriptor))) == + CHF_NULL_DESCRIPTOR) + cc = CHF_F_MALLOC; + + else if((_chf_context.handler_stack = + (ChfHandlerDescriptor *) + malloc((size_t)handler_stack_size*sizeof(ChfHandlerDescriptor))) == + (ChfHandlerDescriptor *)NULL) + { + free(_chf_context.condition_stack); + cc = CHF_F_MALLOC; + } + + else if((_chf_context.message_buffer = + (ChfChar *)malloc( + (size_t)(CHF_MAX_MESSAGE_LENGTH) * sizeof(ChfChar))) == (ChfChar *)NULL) + { + free(_chf_context.condition_stack); + free(_chf_context.handler_stack); + cc = CHF_F_MALLOC; + } + + else +#else + /* Reentrant init: condition_stack, handler_stack, message_buffer + are not needed in the master Chf context. + Init the Chf data key instead. + */ + _chf_context.condition_stack = CHF_NULL_DESCRIPTOR; + _chf_context.handler_stack = (ChfHandlerDescriptor *)NULL; + _chf_context.message_buffer = (char *)NULL; + + if(pthread_key_create(&data_key, DestroyContext)) + ChfAbort(CHF_ABORT_PTHREAD); +#endif + + { + /* Initialize the CHF context */ + _chf_context.app_name = app_name; + _chf_context.options = options; + _chf_context.mrs_data = mrs_data; + _chf_context.mrs_get = mrs_get; + _chf_context.mrs_exit = mrs_exit; + _chf_context.condition_stack_size = condition_stack_size; + _chf_context.handler_stack_size = handler_stack_size; + _chf_context.exit_code = exit_code; + _chf_context.condition_base = _chf_context.condition_sp = + _chf_context.condition_stack; + _chf_context.handler_sp = _chf_context.handler_stack; + _chf_context.state = CHF_IDLE; + +#ifndef _REENTRANT + /* Push the default handler; in the reentrant case, this will be + done once per thread, when the thread-specific context is primed. + */ + ChfPushHandler(DefaultHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER); +#endif + + cc = CHF_S_OK; + } + +#ifdef _REENTRANT + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif + + return cc; +} + + +/* .+ + +.title : ChfExit +.kind : C function +.creation : 24-May-1996 +.description : + This function shuts down CHF and returns nothing to the caller; after + calling ChfExit() the application can continue, but any subsequent call + to any other CHF function, except the inizialization functions, will abort + the application using ChfAbort() with abort code CHF_ABORT_INIT. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been initialized. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_PTHREAD + if a pthread operation fails. + +.call : + ChfExit(); +.input : + void +.output : + void +.status_codes : + none +.notes : + 1.1, 24-May-1996, creation + 2.1, 19-May-2000, update: + - added multithreading support + +.- */ +void ChfExit( + void +) +{ + /* Check that CHF has been correctly initialized */ +#ifndef _REENTRANT + if(_chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); +#else + /* Reentrant check; lock context_mutex first */ + if(pthread_mutex_lock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + if(_chf_context.state == CHF_UNKNOWN) + { + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); + ChfAbort(CHF_ABORT_INIT); + } +#endif + + /* Destroy the context associated with this thread now; this is necessary + to ensure that the context is actually destroyed when a single-threaded + application links with the multithreaded version of Chf: in this case, + pthread_exit() is called *after* ChfExit(), the Chf data key no longer + exists when pthread_exit() is called and the destructor registered + with pthread_key_create() does not take place. + The data pointer associated with the Chf data key is set to NULL to + avoid any subsequent reactivation of the destructor. + */ +#ifdef _REENTRANT + DestroyContext(&chf_context); + if(pthread_setspecific(data_key, (void *)NULL)) + { + (void)pthread_mutex_unlock(&context_mutex); + ChfAbort(CHF_ABORT_PTHREAD); + } +#endif + + /* Shut down the message retrieval subsystem first */ + _chf_context.mrs_exit(_chf_context.mrs_data); + +#ifndef _REENTRANT + /* Free the dynamic memory previously allocated */ + free(_chf_context.message_buffer); + free(_chf_context.handler_stack); + free(_chf_context.condition_stack); +#else + /* Destroy the Chf data key */ + if(pthread_key_delete(data_key)) ChfAbort(CHF_ABORT_PTHREAD); +#endif + + /* Reset CHF state to prevent subsequent calls to ChfExit() itself */ + _chf_context.state = CHF_UNKNOWN; + +#ifdef _REENTRANT + if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD); +#endif +} + + +/* .+ + +.title : _ChfGetContext +.kind : C function +.creation : 19-May-2000 +.description : + This function dynamically primes a new Chf context for the calling + thread (if necessary), and returns a pointer to that context to + the caller. If something does wrong, it aborts the application + with ChfAbort(CHF_ABORT_GET_CONTEXT). + + Results are unpredictable if this function is called before a + successful call to ChfInit(). + +.call : + context = _ChfGetContext(void); +.input : +.output : + ChfContext *context, per-thread Chf context +.status_codes : + none +.notes : + 2.1, 19-May-2000, creation + +.- */ +ChfContext *_ChfGetContext( + void +) +{ + ChfContext *context; + +#ifndef _REENTRANT + /* This function is doomed to fail if _REENTRANT is not defined */ + ChfAbort(CHF_ABORT_GET_CONTEXT); + return((ChfContext *)NULL); +#else + /* Get the thread-specific context pointer associated with the + CHF data key */ + if((context = (ChfContext *)pthread_getspecific(data_key)) + == (ChfContext *)NULL) + { + /* No context pointer; prime a new one, cloning the master context */ + if((context = (ChfContext *)malloc(sizeof(ChfContext))) + == (ChfContext *)NULL) + ChfAbort(CHF_ABORT_GET_CONTEXT); + + memcpy(context, &_chf_context, sizeof(ChfContext)); + + /* Allocate per-thread stacks and message buffer */ + if((context->condition_stack = + (ChfDescriptor *) + malloc((size_t)(context->condition_stack_size+1) + *sizeof(ChfDescriptor))) == CHF_NULL_DESCRIPTOR) + ChfAbort(CHF_ABORT_GET_CONTEXT); + + if((context->handler_stack = + (ChfHandlerDescriptor *) + malloc((size_t)(context->handler_stack_size) + *sizeof(ChfHandlerDescriptor))) == (ChfHandlerDescriptor *)NULL) + { + free(context->condition_stack); + ChfAbort(CHF_ABORT_GET_CONTEXT); + } + + if((context->message_buffer = + (char *) + malloc((size_t)(CHF_MAX_MESSAGE_LENGTH))) == (char *)NULL) + { + free(context->condition_stack); + free(context->handler_stack); + ChfAbort(CHF_ABORT_GET_CONTEXT); + } + + /* Initialize stack pointers */ + context->condition_base = context->condition_sp = + context->condition_stack; + context->handler_sp = context->handler_stack; + + /* Set the thread-specific context pointer; this must be done + before invoking any other function using the context, + including ChfPushHandler() below. + */ + if(pthread_setspecific(data_key, context)) + ChfAbort(CHF_ABORT_GET_CONTEXT); + + /* Push the default handler */ + ChfPushHandler(DefaultHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER); + } + + return context; +#endif +} diff --git a/Chf/chf_msgc.c b/Chf/chf_msgc.c new file mode 100644 index 0000000..d8f712f --- /dev/null +++ b/Chf/chf_msgc.c @@ -0,0 +1,201 @@ +/* .+ + +.identifier : $Id: chf_msgc.c,v 2.2 2001/01/25 14:06:47 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_msgc.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 17-May-1996 +.keywords : * +.description : + This module contains the CHF initialization function ChfMsgcatInit() + +.include : Chf.h + +.notes : + $Log: chf_msgc.c,v $ + Revision 2.2 2001/01/25 14:06:47 cibrario + Added partial Win32 support (Windows CE only). + + Revision 1.3 1996/06/21 14:19:22 cibrario + Bug fix: the private context of the message retrieval facility was + never freed by ExitMessage() + + Revision 1.1 1996/05/28 12:55:15 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_msgc.c,v 2.2 2001/01/25 14:06:47 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#ifndef _WIN32 +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- + Private type definitions + ------------------------------------------------------------------------- */ + +#ifndef _WIN32 +typedef struct +{ + nl_catd catalog; /* Message catalog descriptor */ +} + ChfMsgcatContext; +#endif + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + + +#ifndef _WIN32 +static const char *GetMessage( + void *private_context, + const int module_id, + const int condition_code, + const char *default_message +) +{ + return(catgets(((ChfMsgcatContext *)private_context)->catalog, module_id, + condition_code, default_message)); +} + +static void ExitMessage( + void *private_context +) +{ + (void)catclose(((ChfMsgcatContext *)private_context)->catalog); + free(private_context); +} +#endif + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfMsgcatInit +.kind : C function +.creation : 17-May-1996 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully either ChfMsgcatInit() or one of the + other CHF initialization routines before using any other CHF function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + + WIN32: + + - this function is not available due to lack of system support, and + always returns CHF_F_NOT_AVAILABLE + +.call : + cc = ChfMsgcatInit(app_name, options, + msgcat_name, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const char *app_name, Application's name + const ChfOptions options, Options + const char *msgcat_name, Name of the message catalog + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_SETLOCALE, setlocale() failed + CHF_F_CATOPEN, catopen() failed + CHF_F_MALLOC, FATAL, memory allocation failed + CHF_F_NOT_AVAILABLE, FATAL, function not available +.notes : + 1.1, 17-May-1996, creation + 2.2, 22-Jan-2001, update: + - added Win32 support + +.- */ +int ChfMsgcatInit( /* Initialization with msgcat subsystem */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfChar *msgcat_name, /* Name of the message catalog */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ +#ifdef _WIN32 + /* This function always fails in _WIN32, because message catalogs + are not supported. + */ + return CHF_F_NOT_AVAILABLE; + +#else + ChfMsgcatContext *private_context; + int cc; + + if((private_context = + (ChfMsgcatContext *)malloc(sizeof(ChfMsgcatContext))) == + (ChfMsgcatContext *)NULL) + cc = CHF_F_MALLOC; + + else if(setlocale(LC_ALL, "") == (char *)NULL) + { + free(private_context); + cc = CHF_F_SETLOCALE; + } + + else if((private_context->catalog = catopen(msgcat_name, 0)) == + (nl_catd)(-1)) + { + free(private_context); + cc = CHF_F_CATOPEN; + } + + else if((cc = ChfInit(app_name, options, (void *)private_context, + GetMessage, ExitMessage, condition_stack_size, handler_stack_size, + exit_code)) != CHF_S_OK) + { + (void)catclose(private_context->catalog); + free(private_context); + } + + else + cc = CHF_S_OK; + + return cc; + +#endif +} diff --git a/Chf/chf_sig.c b/Chf/chf_sig.c new file mode 100644 index 0000000..d780447 --- /dev/null +++ b/Chf/chf_sig.c @@ -0,0 +1,402 @@ +/* .+ + +.identifier : $Id: chf_sig.c,v 2.2 2001/01/25 14:07:42 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_sig.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-May-1996 +.keywords : * +.description : + This module implements the condition signalling function of CHF + +.include : Chf.h + +.notes : + $Log: chf_sig.c,v $ + Revision 2.2 2001/01/25 14:07:42 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 14:31:28 cibrario + - Fixed spelling of CHF_SIGNALLING -> CHF_SIGNALING + - Replaced longjmp() with siglongjmp() + - New ChfAction code CHF_UNWIND_KEEP: ChfSignal() unwinds the + execution stack, but keeps the topmost condition group on the + condition stack + + Revision 1.6 1997/01/15 13:34:45 cibrario + Fixed a wrong adjustment of the condition handler stack pointer after + an unwind operation. + Updated the condition handler calls in order to pass to the handlers the + private handler context pointer. + + Revision 1.1 1996/05/28 12:55:51 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_sig.c,v 2.2 2001/01/25 14:07:42 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* .+ + +.title : ChfSignal +.kind : C function +.creation : 10-May-1996 +.description : + This function signals the topmost condition group currently in the + condition stack, and performs the actions requested by the condition + handlers. + + NOTE: This function uses the CHF function 'ChfAbort()' to + abort the application if either + - CHF has not been initialized correctly (abort code CHF_ABORT_INIT) + - the last handler on the handler stack has returned an invalid action + code (abort code CHF_ABORT_INVALID_ACTION) + - one of the handlers has requested the CHF_UNWIND action while + CHF was already unwinding (abort code CHF_ABORT_ALREADY_UNWINDING) + - a CHF_FATAL condition was signalled while CHF was unwinding + (abort code CHF_ABORT_FATAL_UNWINDING) + - all the handlers refused to handle a condition (abort code + CHF_ABORT_IMPROPERLY_HANDLED) + +.call : + ChfSignal(); +.input : + void +.output : + void +.status_codes : + (*) CHF_F_COND_STACK_FULL, the condition stack is full + (*) CHF_F_INVALID_ACTION, invalid handler action (%d) +.notes : + 1.1, 10-May-1996, creation + 1.6, 15-Jan-1997, update & bug fix: + - fixed a wrong adjustment of the condition handler stack pointer after + an unwind operation. + - updated the condition handler calls in order to pass to the handlers the + private handler context pointer, too. + 2.1, 19-May-2000, update: + - added support for structured condition handling + +.- */ +void ChfSignal( + void +) +{ + ChfState saved_state; + ChfDescriptor *saved_condition_base; + ChfDescriptor *current_condition; + ChfHandlerDescriptor *saved_handler_sp; + ChfHandlerDescriptor *handler_up; + ChfHandlerDescriptor *unwind_handler; + ChfAction handler_result; + + /* Check that CHF has been correctly initialized and save the current CHF + state. If CHF was CHF_IDLE change state to CHF_SIGNALING, else if CHF was + CHF_UNWINDING change to CHF_SIGNAL_UNWINDING, otherwise remain in the + previous state (that must be CHF_SIGNALING) + */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + saved_state = chf_context.state; + + if(chf_context.state == CHF_IDLE) + chf_context.state = CHF_SIGNALING; + else if(chf_context.state == CHF_UNWINDING) + chf_context.state = CHF_SIGNAL_UNWINDING; + + if(chf_context.condition_sp > chf_context.condition_base) + { + /* Save the base of the current condition group and then update it in + order to allow further generation of conditions inside the condition + handlers that will be called soon. + */ + current_condition = chf_context.condition_sp-1; + saved_condition_base = chf_context.condition_base; + chf_context.condition_base = chf_context.condition_sp; + + /* Save the current condition handler pointer */ + saved_handler_sp = chf_context.handler_sp; + + /* Call the condition handlers; the loop will exit either: + - when the handler stack is empty, or + - when the current handler returns either CHF_CONTINUE or CHF_UNWIND + */ + handler_result = CHF_RESIGNAL; + while(handler_result == CHF_RESIGNAL && + chf_context.handler_sp > chf_context.handler_stack) + { + chf_context.handler_sp--; + + /* The current condition handler, described by chf_context.handler_sp, + can recursively invoke ChfGenerate() and ChfSignal(). + + ChfGenerate() will store the new condition group starting from + chf_context.condition_sp, that points to the first free slot + of the condition stack. During the first generation, since + chf_context.condition_sp == chf_context.condition_base, the + link pointer of the condition will be NULL and, therefore, + the condition will be the first of a new condition group. + + ChfSignal() will signal the condition group described by the + stack block from chf_context.condition_base to + chf_context.condition_sp-1, if it contains at least one condition; + it will call the handlers starting from chf_context.handler_sp-1, + that describes the handler immediately preceding the current handler. + */ + handler_result = chf_context.handler_sp->handler( + current_condition, chf_context.state, + chf_context.handler_sp->handler_context); + + /* When the CHF state is CHF_SIGNALING, any condition group generated + but not yet signalled when the current handler exits must be merged + with the condition group currently being signalled, in order to allow + the condition handlers to add their own conditions to the condition + group. If the severity of the previous condition group was CHF_FATAL, + the severity of the new group is forced to CHF_FATAL, too. + + When the CHF state is CHF_UNWINDING, the condition group for + which the UNWIND has been requested is 'frozen', no further + modifications are allowed on it, and the condition group is simply + discarded. + */ + if(chf_context.condition_sp > chf_context.condition_base) + { + if(chf_context.state == CHF_SIGNALING) + { + /* Force the new severity to CHF_FATAL if necessary */ + if(ChfGetSeverity(current_condition) == CHF_FATAL) + ChfGetSeverity(chf_context.condition_sp-1) = CHF_FATAL; + + /* Link together the condition groups */ + chf_context.condition_base->next = current_condition; + current_condition = chf_context.condition_sp-1; + chf_context.condition_base = chf_context.condition_sp; + } + + else + chf_context.condition_sp = chf_context.condition_base; + } + + /* The action CHF_CONTINUE is not allowed if the current condition + severity is CHF_FATAL; it's automatically changed to CHF_RESIGNAL + */ + if(handler_result == CHF_CONTINUE && + ChfGetSeverity(current_condition) == CHF_FATAL) + handler_result = CHF_RESIGNAL; + } + + /* Perform the action requested by the last condition handler invoked */ + switch(handler_result) + { + case CHF_CONTINUE: + { + /* Restore the handler stack pointer; the next ChfSignal() invoked + from our same nesting level will invoke our same handler chain + again. + */ + chf_context.handler_sp = saved_handler_sp; + + /* Discard the current condition group */ + chf_context.condition_base = chf_context.condition_sp = + saved_condition_base; + + /* Restore che saved CHF state */ + chf_context.state = saved_state; + + /* Continue from the instruction following the ChfSignal() */ + break; + } + + case CHF_UNWIND: + case CHF_UNWIND_KEEP: + { + /* Unwind the execution stack. Check that another unwind isn't + already in progress + */ + if(chf_context.state == CHF_UNWINDING) + ChfAbort(CHF_ABORT_ALREADY_UNWINDING); + + else + { + /* Change CHF state */ + chf_context.state = CHF_UNWINDING; + + /* chf_context.handler_sp points to the condition handler that + has requested the unwind; call all the handlers again, starting + from saved_handler_sp (top of the handler stack) up to and + including chf_context.handler_sp. + */ + handler_up = saved_handler_sp; + + while(handler_up > chf_context.handler_sp) + { + ChfAction unw_handler_result; + handler_up--; + + /* The current condition handler, described by handler_up + can recursively invoke ChfGenerate() and ChfSignal(). + + ChfGenerate() will store the new condition group starting from + chf_context.condition_sp, that points to the first free slot + of the condition stack. During the first generation, since + chf_context.condition_sp == chf_context.condition_base, the + link pointer of the condition will be NULL and, therefore, + the condition will be the first of a new condition group. + + ChfSignal() will generate the condition group described by the + stack block from chf_context.condition_base to + chf_context.condition_sp-1, if it contains at least one + condition; it will call the handlers starting from + chf_context.handler_sp-1, that describes the handler + immediately preceding the handler that has requested the unwind. + + Further unwind requests are not allowed, and will trigger + the condition CHF_F_UNWINDING + */ + unw_handler_result = handler_up->handler( + current_condition, chf_context.state, + handler_up->handler_context); + + /* When the CHF state is CHF_UNWINDING, any condition group + generated but not yet signalled when the current handler + returns must be discarded + */ + chf_context.condition_sp = chf_context.condition_base; + } + + /* Restore the handler stack pointer, discarding the unwinded + condition handlers. chf_context.handler_sp points to the + handler that requested the unwind; that handler has been + unwinded and its location is now the first free slot in the + condition handler stack. + */ + unwind_handler = chf_context.handler_sp; + + if(handler_result == CHF_UNWIND) + { + /* Normal unwind: + restore the condition stack pointers, discarding all condition + groups. + */ + chf_context.condition_base = chf_context.condition_sp = + chf_context.condition_stack; + } + else + { + /* Special unwind for structured condition handling: + restore the condition_base pointer only, to keep the + topmost condition group on the condition stack. This way, + the condition group remains accessible after the unwind. + */ + chf_context.condition_base = saved_condition_base; + } + + /* Change the CHF state to CHF_IDLE, and execute longjmp(). + If the handler hasn't a valid unwind_context associated with it, + simply abort the application. + */ + chf_context.state = CHF_IDLE; + + if(unwind_handler->unwind_context == CHF_NULL_CONTEXT) + ChfAbort(CHF_ABORT_SILENT); + else + ChfSiglongjmp(unwind_handler->unwind_context, 1); + } + + break; + } + + case CHF_RESIGNAL: + { + ChfAbort( + (chf_context.state == CHF_SIGNALING) ? + CHF_ABORT_IMPROPERLY_HANDLED : CHF_ABORT_FATAL_UNWINDING); + + break; + } + + default: + { + /* Invalid handler action detected; generate and immediately signal a + condition if the broken handler isn't the last handler on the stack, + otherwise call ChfAbort() + */ + if(chf_context.handler_sp > chf_context.handler_stack) + { + ChfCondition CHF_F_INVALID_ACTION, CHF_FATAL, handler_result + ChfEnd; + + ChfSignal(); + } + + else + ChfAbort(CHF_ABORT_INVALID_ACTION); + + break; + } + } + } + + /* Restore the old CHF state */ + chf_context.state = saved_state; +} + + +/* .+ + +.title : ChfDiscard +.kind : C function +.creation : 17-May-1996 +.description : + This function discards the topmost condition group currently in the + condition stack, without signalling it. The function does nothing if + the condition stack is empty. + + NOTE: This function uses the CHF function 'ChfAbort()' to + abort the application if either + - CHF has not been initialized correctly (abort code CHF_ABORT_INIT) + +.call : + ChfDiscard(); +.input : + void +.output : + void +.status_codes : + none +.notes : + 1.1, 17-May-1996, creation + +.- */ +void ChfDiscard( /* Discard the current conditions */ + void +) +{ + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + /* Reset the current condition stack pointer to the current condition + stack base pointer + */ + chf_context.condition_sp = chf_context.condition_base; +} diff --git a/Chf/chf_st.c b/Chf/chf_st.c new file mode 100644 index 0000000..5f2134b --- /dev/null +++ b/Chf/chf_st.c @@ -0,0 +1,227 @@ +/* .+ + +.identifier : $Id: chf_st.c,v 2.2 2001/01/25 14:08:45 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_st.c,v $, condition generation +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 24-May-1996 +.keywords : * +.description : + This module implements the CHF initialization function ChfStaticInit() + +.include : Chf.h + +.notes : + $Log: chf_st.c,v $ + Revision 2.2 2001/01/25 14:08:45 cibrario + Added partial Win32 support (Windows CE only). + + Revision 1.1 1996/05/28 12:56:14 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_st.c,v 2.2 2001/01/25 14:08:45 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- + Private type definitions + ------------------------------------------------------------------------- */ + +typedef struct +{ + const ChfTable *table; + size_t size; +} + ChfStaticContext; + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +/* Win32 does not have bsearch(); + provide a simple one from glibc here. +*/ +static void *bsearch( + const void *key, + const void *base, + size_t nmemb, + size_t size, + int (*compar)(const void *, const void *) +) +{ + size_t l, u, idx; + const void *p; + int comparison; + + l = 0; + u = nmemb; + while (l < u) + { + idx = (l + u) / 2; + p = (void *) (((const char *) base) + (idx * size)); + comparison = (*compar) (key, p); + if (comparison < 0) + u = idx; + else if (comparison > 0) + l = idx + 1; + else + return (void *) p; + } + + return NULL; +} +#endif + +#define GT 1 +#define LT -1 +#define EQ 0 + +static int Search( + const void *l, + const void *r +) +{ + if(((ChfTable *)l)->module > ((ChfTable *)r)->module) + return(GT); + + else if(((ChfTable *)l)->module < ((ChfTable *)r)->module) + return(LT); + + else if(((ChfTable *)l)->code > ((ChfTable *)r)->code) + return(GT); + + else if(((ChfTable *)l)->code < ((ChfTable *)r)->code) + return(LT); + + return(EQ); +} + +static const ChfChar *StGetMessage( + void *private_context, + const int module_id, + const int condition_code, + const ChfChar *default_message +) +{ + ChfTable key; + ChfTable *res; + + key.module = module_id; + key.code = condition_code; + + if((res = bsearch(&key, ((ChfStaticContext *)private_context)->table, + ((ChfStaticContext *)private_context)->size, sizeof(ChfTable), Search)) == + (void *)NULL) + return(default_message); + + return(((ChfTable *)res)->msg_template); +} + +static void ExitMessage( + void *private_context +) +{ +} + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfStaticInit +.kind : C function +.creation : 24-May-1996 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully either ChfStaticInit() or one of the + other CHF initialization routines before using any other CHF function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + +.call : + cc = ChfStaticInit(app_name, options, + table, table_size, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const char *app_name, Application's name + const ChfOptions options, Options + const ChfTable *table, pointer to the static message table + const size_t table_size, size of the table (# of entries) + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_MALLOC, FATAL, memory allocation failed +.notes : + 1.1, 27-May-1996, creation + +.- */ +int ChfStaticInit( /* Initialization with static message tables */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ + const ChfTable *table, /* Static message table */ + const size_t table_size, /* Size of the message table */ + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ + ChfStaticContext *private_context; + int cc; + + if((private_context = + (ChfStaticContext *)malloc(sizeof(ChfStaticContext))) == + (ChfStaticContext *)NULL) + cc = CHF_F_MALLOC; + + else if((cc = ChfInit(app_name, options, (void *)private_context, + StGetMessage, ExitMessage, condition_stack_size, handler_stack_size, + exit_code)) != CHF_S_OK) + free(private_context); + + else + { + private_context->table = table; + private_context->size = table_size; + cc = CHF_S_OK; + } + + return cc; +} diff --git a/Chf/chf_top.c b/Chf/chf_top.c new file mode 100644 index 0000000..da6b824 --- /dev/null +++ b/Chf/chf_top.c @@ -0,0 +1,115 @@ +/* .+ + +.identifier : $Id: chf_top.c,v 2.2 2001/01/25 14:09:21 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_top.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 5-Jun-1996 +.keywords : * +.description : + This module implements the CHF function ChfGetTopCondition() + +.include : Chf.h + +.notes : + $Log: chf_top.c,v $ + Revision 2.2 2001/01/25 14:09:21 cibrario + Added partial Win32 support (Windows CE only). + + Revision 2.1 2000/05/26 14:23:33 cibrario + ChfGetTopCondition() used to return a pointer to the wrong condition + descriptor; fixed. + + Revision 1.2 1996/06/11 12:47:17 cibrario + file creation + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_top.c,v 2.2 2001/01/25 14:09:21 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfGetTopCondition +.kind : C function +.creation : 5-Jun-1996 +.description : + This function returns to the caller a pointer to the top condition of + the current condition group. It generates and immediately signals the + condition CHF_F_BAD_STATE if the current condition group is empty. + + + NOTE: During condition signalling, CHF creates a new, empty, condition group + immediately before starting the invocation sequence of the condition + handlers, as described in the documentation. Therefore + ChfGetTopCondition(), if called from a condition handler, will return + a pointer to the top condition generated during the handling ONLY, and + NOT to the top condition of the condition group being signalled. The + latter pointer is directly available, as an argument, to the condition + handlers. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT + if CHF hasn't been correctly initialized. + + NOTE: The returned pointer is no longer valid when any other CHF function + is called after ChfGetTopCondition(). + +.call : + d = ChfGetTopCondition(); +.input : + void +.output : + const ChfDescriptor *d, condition descriptor +.status_codes : + +.notes : + 1.2, 17-May-1996, creation + 2.1, 24-May-2000, bug fix: + - condition stack referenced incorrectly + +.- */ +const ChfDescriptor *ChfGetTopCondition( /* Retrieve top condition */ + void +) +{ + ChfDescriptor *d; + + /* Check that CHF has been correctly initialized */ + if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT); + + if((d = chf_context.condition_sp) == chf_context.condition_base) + { + ChfCondition CHF_F_BAD_STATE, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* The top element of the condition group is the element immediately + below the stack pointer. + */ + return d-1; +} diff --git a/Chf/chf_win32.c b/Chf/chf_win32.c new file mode 100644 index 0000000..7fd7eaf --- /dev/null +++ b/Chf/chf_win32.c @@ -0,0 +1,191 @@ +/* .+ + +.identifier : $Id: chf_win32.c,v 2.2 2001/01/25 14:11:58 cibrario Exp $ +.context : CHF, Condition Handling Facility +.title : $RCSfile: chf_win32.c,v $, Win32 initialization function +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 19-Jan-2001 +.keywords : * +.description : + This module contains the CHF initialization function ChfWin32Init() + +.include : Chf.h + +.notes : + $Log: chf_win32.c,v $ + Revision 2.2 2001/01/25 14:11:58 cibrario + *** empty log message *** + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: chf_win32.c,v 2.2 2001/01/25 14:11:58 cibrario Exp $"; +#endif + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "Chf.h" +#include "ChfPriv.h" + + +/* ------------------------------------------------------------------------- + Global and static variables + ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- + Private type definitions + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +typedef struct +{ + HINSTANCE instance; /* App. instance handle */ + ChfChar buffer[CHF_MAX_MESSAGE_LENGTH]; /* Temporary buffer */ +} + ChfWin32Context; +#endif + + +/* ------------------------------------------------------------------------- + Private functions + ------------------------------------------------------------------------- */ + +#ifdef _WIN32 +static const ChfChar *Win32GetMessage( + void *private_context, + const int module_id, + const int condition_code, + const ChfChar *default_message +) +{ + if(!LoadString( + ((ChfWin32Context *)private_context)->instance, + module_id*1000 + condition_code, + ((ChfWin32Context *)private_context)->buffer, + CHF_MAX_MESSAGE_LENGTH-1)) + return default_message; + + return ((ChfWin32Context *)private_context)->buffer; +} + +static void ExitMessage( + void *private_context +) +{ + free(private_context); +} +#endif + + +/* ------------------------------------------------------------------------- + Public functions + ------------------------------------------------------------------------- */ + +/* .+ + +.title : ChfWin32Init +.kind : C function +.creation : 19-Jan-2001 +.description : + This function initializes CHF and returns to the caller a condition code; + that code will be either CHF_S_OK if the initialization was succesful, + or one of the other values listed below. + + It's necessary to invoke succesfully either ChfWin32Init() or one of the + other CHF initialization routines before using any other CHF function. + + NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT + if CHF has already been initialized before. + + WIN32: + + - This function is available in Win32 only; it will return + CHF_F_NOT_AVAILABLE on Unix platforma. + + - message retrieval is done through the LoadString() Win32 function. + This function does not support message sets, so the linear message id + passed to it is made by module_id*1000 + condition_code. The following + limits are in effect: + 0 <= condition_code <= 999 + 0 <= module_id <= 64 + +.call : + cc = ChfWin32Init(app_name, options, + msgcat_name, + condition_stack_size, handler_stack_size, + exit_code); +.input : + const ChfChar *app_name, Application's name + const ChfOptions options, Options + HINSTANCE instance, App. instance handle + const int condition_stack_size, Size of the condition stack + const int handler_stack_size, Size of the handler stack + const int exit_code, Abnormal exit code +.output : + int cc, condition code +.status_codes : + CHF_F_MALLOC, FATAL, memory allocation failed + CHF_F_NOT_AVAILABLE, FATAL, function not available +.notes : + 2.2, 19-Jan-2001, creation + +.- */ +int ChfWin32Init( /* Initialization within _WIN32 */ + const ChfChar *app_name, /* Application's name */ + const ChfOptions options, /* Options */ +#ifndef _WIN32 + void *instance, /* Fake arguments */ +#else + HINSTANCE instance, /* App. instance handle */ +#endif + const int condition_stack_size, /* Size of the condition stack */ + const int handler_stack_size, /* Size of the handler stack */ + const int exit_code /* Abnormal exit code */ +) +{ +#ifndef _WIN32 + /* This function is available only in Win32 */ + return CHF_F_NOT_AVAILABLE; + +#else + ChfWin32Context *private_context; + int cc; + + if((private_context = + (ChfWin32Context *)malloc(sizeof(ChfWin32Context))) == + (ChfWin32Context *)NULL) + cc = CHF_F_MALLOC; + + else if((cc = ChfInit(app_name, options, (void *)private_context, + Win32GetMessage, ExitMessage, condition_stack_size, handler_stack_size, + exit_code)) != CHF_S_OK) + { + free(private_context); + } + + else + { + /* Save Win32 specific context items into private Chf context */ + private_context->instance = instance; + + cc = CHF_S_OK; + } + + return cc; + +#endif +} diff --git a/Chf/libChf.vcp b/Chf/libChf.vcp new file mode 100755 index 0000000..09f0fc0 --- /dev/null +++ b/Chf/libChf.vcp @@ -0,0 +1,485 @@ +# Microsoft eMbedded Visual Tools Project File - Name="libChf" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE x86em) Static Library" 0x7f04 +# TARGTYPE "Win32 (WCE ARM) Static Library" 0x8504 + +CFG=libChf - Win32 (WCE ARM) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libChf.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libChf.vcn" CFG="libChf - Win32 (WCE ARM) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libChf - Win32 (WCE ARM) Release" (based on "Win32 (WCE ARM) Static Library") +!MESSAGE "libChf - Win32 (WCE ARM) Debug" (based on "Win32 (WCE ARM) Static Library") +!MESSAGE "libChf - Win32 (WCE x86em) Release" (based on "Win32 (WCE x86em) Static Library") +!MESSAGE "libChf - Win32 (WCE x86em) Debug" (based on "Win32 (WCE x86em) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMRel" +# PROP BASE Intermediate_Dir "ARMRel" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "ARMRel" +# PROP Intermediate_Dir "ARMRel" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=clarm.exe +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /Oxs /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "NDEBUG" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /Oxs /M$(CECrtMT) /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMDbg" +# PROP BASE Intermediate_Dir "ARMDbg" +# PROP BASE CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "ARMDbg" +# PROP Intermediate_Dir "ARMDbg" +# PROP CPU_ID "{D6518FFC-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=clarm.exe +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "ARM" /D "_ARM_" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_LIB" /YX /M$(CECrtMTDebug) /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "X86EMRel" +# PROP BASE Intermediate_Dir "X86EMRel" +# PROP BASE CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "X86EMRel" +# PROP Intermediate_Dir "X86EMRel" +# PROP CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_LIB" /YX /Oxs /Gz /c +# ADD CPP /nologo /W3 /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_LIB" /YX /Oxs /Gz /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "X86EMDbg" +# PROP BASE Intermediate_Dir "X86EMDbg" +# PROP BASE CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "X86EMDbg" +# PROP Intermediate_Dir "X86EMDbg" +# PROP CPU_ID "{D6518FF4-710F-11D3-99F2-00105A0DF099}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_LIB" /YX /Gz /c +# ADD CPP /nologo /W3 /Zi /Od /D "DEBUG" /D _WIN32_WCE=$(CEVersion) /D "WIN32" /D "STRICT" /D "_WIN32_WCE_EMULATION" /D "INTERNATIONAL" /D "USA" /D "INTLMSG_CODEPAGE" /D "$(CePlatform)" /D "i486" /D UNDER_CE=$(CEVersion) /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_LIB" /FR /YX /Gz /c +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo + +!ENDIF + +# Begin Target + +# Name "libChf - Win32 (WCE ARM) Release" +# Name "libChf - Win32 (WCE ARM) Debug" +# Name "libChf - Win32 (WCE x86em) Release" +# Name "libChf - Win32 (WCE x86em) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\chf_abrt.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_A=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_gen.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_G=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_hdlr.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_H=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_init.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_I=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_msgc.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_M=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_sig.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_S=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_st.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_ST=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_top.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_T=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chf_win32.c + +!IF "$(CFG)" == "libChf - Win32 (WCE ARM) Release" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE ARM) Debug" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Release" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ELSEIF "$(CFG)" == "libChf - Win32 (WCE x86em) Debug" + +DEP_CPP_CHF_W=\ + ".\Chf.h"\ + ".\ChfPriv.h"\ + + +!ENDIF + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Chf.h +# End Source File +# Begin Source File + +SOURCE=.\ChfPriv.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "rc" +# Begin Source File + +SOURCE=.\chf.rc +# End Source File +# End Group +# End Target +# End Project diff --git a/Chf/resource.h b/Chf/resource.h new file mode 100755 index 0000000..7940d7c --- /dev/null +++ b/Chf/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by chf.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Chf/test01.c b/Chf/test01.c new file mode 100644 index 0000000..fa3c177 --- /dev/null +++ b/Chf/test01.c @@ -0,0 +1,93 @@ +/* $Id: test01.c,v 2.1 2000/05/29 13:55:50 cibrario Rel $ + Chf test program. + Simple initialization. + + +*/ + +#include +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + + +int main(int argc, char *argv[]) +{ + int st; + const char *msg; + const ChfDescriptor *d, *e; + + puts("test01"); + + system("gencat test01.cat test01.msf"); + system("gencat test01.cat chf.msf"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + + /* ChfGetMessage: + message (CHF_MODULE_ID, 1) exists, (CHF_MODULE_ID, 2) does not + */ + msg = ChfGetMessage(CHF_MODULE_ID, 1, "Default_1"); + if(strcmp(msg, "Set_255,Message_1")) exit(EXIT_FAILURE); + msg = ChfGetMessage(CHF_MODULE_ID, 2, "Default_2"); + if(strcmp(msg, "Default_2")) exit(EXIT_FAILURE); + + /* Generate a condition and check descriptor; this is line 46 */ + ChfCondition 3, CHF_WARNING, 456 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 3 + || d->severity != CHF_WARNING + || d->line_number != 46 + || strcmp(d->file_name, "test01.c") + || strcmp(d->message, "Set_255,Arg_456,Message_3") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Generate another condition and check; this is line 60 */ + ChfCondition 4, CHF_INFO, "arg" ChfEnd; + + if((e = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + e->module_id != CHF_MODULE_ID + || e->condition_code != 4 + || e->severity != CHF_INFO + || e->line_number != 60 + || strcmp(e->file_name, "test01.c") + || strcmp(e->message, "Set_255,Arg_arg,Message_4") + || e->next != d + ) exit(EXIT_FAILURE); + + /* Discard the previous condition group and create a new one */ + ChfDiscard(); + + /* This is line 77 */ + ChfCondition 5, CHF_ERROR, 456, 789 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 5 + || d->severity != CHF_ERROR + || d->line_number != 77 + || strcmp(d->file_name, "test01.c") + || strcmp(d->message, "Set_255,Arg_456-789,Message_5") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Exit Chf */ + ChfExit(); + exit(EXIT_SUCCESS); +} diff --git a/Chf/test01.msf b/Chf/test01.msf new file mode 100644 index 0000000..984abf0 --- /dev/null +++ b/Chf/test01.msf @@ -0,0 +1,18 @@ +$set 1 +255 Chf Test + +$set 255 +1 Set_255,Message_1 +3 Set_255,Arg_%d,Message_3 +4 Set_255,Arg_%s,Message_4 +5 Set_255,Arg_%d-%d,Message_5 +6 Set_255,Message_6 (thread %d, sub-condition) +7 Set_255,Message_7 (thread %d, main) +8 Set_255,Message_8 (thread %d, main) +9 Set_255,Message_9 + +10 Invalid descriptor link detected +11 Bad hdlr stack push count %d; should be %d +12 Bad cond stack push count %d; should be %d + +20 Structured exc handling test cond diff --git a/Chf/test02.c b/Chf/test02.c new file mode 100644 index 0000000..6219813 --- /dev/null +++ b/Chf/test02.c @@ -0,0 +1,121 @@ +/* $Id: test02.c,v 2.1 2000/05/29 13:56:44 cibrario Rel $ + Chf test program. + Simple initialization - multithreaded. + + +*/ + +#include +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + + +void *task(void *arg) +{ + const char *msg; + const ChfDescriptor *d, *e; + + printf("\tThread %d\n", (int)arg); + + /* message (CHF_MODULE_ID, 1) exists, (CHF_MODULE_ID, 2) does not */ + msg = ChfGetMessage(CHF_MODULE_ID, 1, "Default_1"); + if(strcmp(msg, "Set_255,Message_1")) exit(EXIT_FAILURE); + msg = ChfGetMessage(CHF_MODULE_ID, 2, "Default_2"); + if(strcmp(msg, "Default_2")) exit(EXIT_FAILURE); + + /* Generate a condition and check descriptor; this is line 36 */ + ChfCondition 3, CHF_WARNING, 456 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 3 + || d->severity != CHF_WARNING + || d->line_number != 36 + || strcmp(d->file_name, "test02.c") + || strcmp(d->message, "Set_255,Arg_456,Message_3") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Generate another condition and check; this is line 50 */ + ChfCondition 4, CHF_INFO, "arg" ChfEnd; + + if((e = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + e->module_id != CHF_MODULE_ID + || e->condition_code != 4 + || e->severity != CHF_INFO + || e->line_number != 50 + || strcmp(e->file_name, "test02.c") + || strcmp(e->message, "Set_255,Arg_arg,Message_4") + || e->next != d + ) exit(EXIT_FAILURE); + + /* Discard the previous condition group and create a new one */ + ChfDiscard(); + + /* This is line 67 */ + ChfCondition 5, CHF_ERROR, 456, 789 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 5 + || d->severity != CHF_ERROR + || d->line_number != 67 + || strcmp(d->file_name, "test02.c") + || strcmp(d->message, "Set_255,Arg_456-789,Message_5") + || d->next != NULL + ) exit(EXIT_FAILURE); + + return (void *)0; +} + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test02"); + +#ifdef _REENTRANT + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + + +void *task(void *arg) +{ + const char *msg; + const ChfDescriptor *d, *e; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + printf("\tThread %d\n", (int)arg); + + /* Generate a condition group and signal it */ + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + ChfSignal(); + + return (void *)0; +} + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; + +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test03"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + +#ifdef _REENTRANT + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +struct tdata_s +{ + const ChfDescriptor *d, *e; + int phase; +}; + +ChfAction h1( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + if(c != tdata_p->e || + ChfGetNextDescriptor(c) != tdata_p->d) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + + else + action = CHF_CONTINUE; + + return action; + +} + +ChfAction h2( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + switch(s) + { + case CHF_SIGNALING: + { + if(c != tdata_p->e + || ChfGetNextDescriptor(c) != tdata_p->d + || (tdata_p->phase != 2 && tdata_p->phase != 4)) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + + else + { + action = (ChfGetConditionCode(c) != 8 ? CHF_CONTINUE : CHF_UNWIND); + } + break; + } + case CHF_UNWINDING: + { + if(tdata_p->phase != 4) exit(EXIT_FAILURE); + tdata_p->phase = 5; + action = CHF_CONTINUE; + break; + } + default: + { + exit(EXIT_FAILURE); + } + } + return action; +} + +ChfAction h3( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + /* This handler must be invoked only during the first signal */ + if(tdata_p->phase != 3) exit(EXIT_FAILURE); + + switch(s) + { + case CHF_SIGNALING: + { + if(ChfGetConditionCode(c) != 9 || + ChfGetNextDescriptor(c) != NULL) + { + exit(EXIT_FAILURE); + } + + else + { + tdata_p->phase = 4; + action = CHF_CONTINUE; + } + break; + } + default: + { + exit(EXIT_FAILURE); + } + } + return action; +} + +ChfAction h4( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + struct tdata_s *tdata_p = (struct tdata_s *)p; + ChfAction action; + + /* This handler must be invoked only during the first signal */ + if(tdata_p->phase != 2) exit(EXIT_FAILURE); + + switch(s) + { + case CHF_SIGNALING: + { + if(c != tdata_p->e + || ChfGetNextDescriptor(c) != tdata_p->d) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + + else + { + /* This generates a new group and signals it */ + tdata_p->phase = 3; + ChfCondition 9, CHF_INFO ChfEnd; + ChfSignal(); + + if(tdata_p->phase != 4) exit(EXIT_FAILURE); + tdata_p->phase = 5; + + if(c != tdata_p->e + || ChfGetNextDescriptor(c) != tdata_p->d) + { + ChfCondition 10, CHF_FATAL ChfEnd; + action = CHF_RESIGNAL; + } + else + action = CHF_CONTINUE; + } + break; + } + default: + { + exit(EXIT_FAILURE); + } + } + return action; +} + +void *task(void *arg) +{ + volatile struct tdata_s tdata; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + printf("\tThread %d\n", (int)arg); + + /* Push the handler */ + ChfPushHandler(h1, NULL, (ChfPointer)(&tdata)); + + /* Generate a condition group and signal it */ + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + /* The sleep() is here to increase contention between threads */ + sleep(1); + ChfSignal(); + + /* Pop the handler */ + ChfPopHandler(); + + /* Generate a new condition group with (apparently) wrong linkage + and signal it; this checks that the handler has actually been + removed. + */ + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = NULL; + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = NULL; + ChfSignal(); + + /* Conditional unwind test */ + { + sigjmp_buf jb; + + tdata.phase = 0; + if(setjmp(jb) == 0) + { + ChfPushHandler(h2, jb, (ChfPointer)(&tdata)); + + /* Generate a condition group and signal it */ + tdata.phase = 1; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + /* This does not trigger an unwind */ + tdata.phase = 2; + ChfSignal(); + + tdata.phase = 3; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 8, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + /* This MUST trigger an unwind */ + tdata.phase = 4; + ChfSignal(); + + exit(EXIT_FAILURE); + + } + else + { + /* Unwind */ + if(tdata.phase != 5) + exit(EXIT_FAILURE); + + ChfPopHandler(); + } + } + + /* Condition generation and signal while a signal is in progress; + this requires two handlers. + */ + { + tdata.phase = 0; + + ChfPushHandler(h3, NULL, (ChfPointer)&tdata); + ChfPushHandler(h4, NULL, (ChfPointer)&tdata); + + tdata.phase = 1; + ChfCondition 6, CHF_INFO, (int)arg ChfEnd; + tdata.d = ChfGetTopCondition(); + ChfCondition 7, CHF_INFO, (int)arg ChfEnd; + tdata.e = ChfGetTopCondition(); + + tdata.phase = 2; + ChfSignal(); + + if(tdata.phase != 5) + exit(EXIT_FAILURE); + + ChfPopHandler(); + ChfPopHandler(); + } + + return (void *)0; +} + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; + +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test04"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", 50, 10, 1)) + exit(st); + +#ifdef _REENTRANT + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +#define H_STACK_SIZE 10 +#define C_STACK_SIZE 30 + +/* Dummy handler; pushed only to verify that the handler stack overflow + checks are correct. +*/ +ChfAction h1( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + return CHF_RESIGNAL; +} + +/* Overflow check handler; it unwinds if the CHF_F_HDLR_STACK_FULL + condition is signalled exactly after H_STACK_SIZE-2 invocations + of ChfPushHandler(), it resignals a modified condition if the + condition is signalled too early +*/ +ChfAction h2( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + int push_count = *((int *)p); + ChfAction action; + + if(s == CHF_SIGNALING) + { + if(ChfGetModuleId(c) == CHF_SET + && ChfGetConditionCode(c) == CHF_F_HDLR_STACK_FULL) + { + /* Handler stack is full; check correctness of the descriptor */ + if(push_count == H_STACK_SIZE-2 + && ChfGetNextDescriptor(c) == NULL + && ChfGetSeverity(c) == CHF_FATAL) + action = CHF_UNWIND; + else + { + ChfCondition 11, CHF_FATAL, push_count, H_STACK_SIZE-2 + ChfEnd; + action = CHF_RESIGNAL; + } + } + } + + else + action = CHF_RESIGNAL; + + return action; +} + +/* Overflow check handler; it unwinds if the CHF_F_COND_STACK_FULL + condition is signalled exactly after C_STACK_SIZE invocations + of ChfCondition, it resignals a modified condition if the + condition is signalled too early +*/ +ChfAction h3( + const ChfDescriptor *c, + const ChfState s, + ChfPointer p +) +{ + int push_count = *((int *)p); + ChfAction action; + + if(s == CHF_SIGNALING) + { + if(ChfGetModuleId(c) == CHF_SET + && ChfGetConditionCode(c) == CHF_F_COND_STACK_FULL) + { + /* Handler stack is full; check correctness of the descriptor */ + if(push_count == C_STACK_SIZE + && ChfGetNextDescriptor(c) == NULL + && ChfGetSeverity(c) == CHF_FATAL) + action = CHF_UNWIND; + else + { + ChfCondition 12, CHF_FATAL, push_count, C_STACK_SIZE + ChfEnd; + action = CHF_RESIGNAL; + } + } + } + + else + action = CHF_RESIGNAL; + + return action; +} + + +void *task(void *arg) +{ + int push_count = 0; + sigjmp_buf jb; + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + printf("\tThread %d\n", (int)arg); + + /* Check handler stack overflow checks */ + if(sigsetjmp(jb, 1) == 0) + { + int i; + + /* Push the handler */ + ChfPushHandler(h2, jb, (ChfPointer)(&push_count)); + + /* The sleep() is here to increase contention between threads */ + sleep(1); + + /* Push dummy handlers until an error should occur */ + for(; push_count +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +#define H_STACK_SIZE 10 +#define C_STACK_SIZE 30 + + +void *task(void *arg) +{ + volatile int phase = 0; + + ChfTry + { + phase = 1; + ChfCondition 20, CHF_SUCCESS ChfEnd; + ChfSignal(); + + phase = 2; + ChfCondition 20, CHF_INFO ChfEnd; + ChfSignal(); + + phase = 3; + ChfCondition 20, CHF_WARNING ChfEnd; + ChfSignal(); + + phase = 4; + ChfCondition 20, CHF_ERROR ChfEnd; + ChfSignal(); + + phase = 5; + ChfCondition 20, CHF_FATAL ChfEnd; + ChfSignal(); + + /* Should not be reached */ + return (void *)EXIT_FAILURE; + } + ChfCatch + { + /* Catched an exception; check descriptor */ + const ChfDescriptor *d = ChfGetTopCondition(); + if(d == NULL + || ChfGetNextDescriptor(d) != NULL + || ChfGetModuleId(d) != CHF_MODULE_ID + || ChfGetConditionCode(d) != 20) + return (void *)EXIT_FAILURE; + } + ChfEndTry; + + /* Check that the condition stack actually is empty after catch */ + ChfTry + { + volatile const ChfDescriptor *e = ChfGetTopCondition(); + } + ChfCatch + { + const ChfDescriptor *d = ChfGetTopCondition(); + if(d == NULL + || ChfGetNextDescriptor(d) != NULL + || ChfGetModuleId(d) != CHF_SET + || ChfGetConditionCode(d) != CHF_F_BAD_STATE) + return (void *)EXIT_FAILURE; + } + ChfEndTry; + + return (void *)EXIT_SUCCESS; +} + + +#define N_THREADS 50 + +int main(int argc, char *argv[]) +{ + int st; + int i; + void *ret; + +#ifdef _REENTRANT + pthread_t t[N_THREADS]; +#endif + + puts("test06"); + + /* Initialization */ + if(st = ChfMsgcatInit(argv[0], CHF_DEFAULT, "./test01.cat", + C_STACK_SIZE, H_STACK_SIZE, EXIT_FAILURE)) + exit(st); + +#ifdef _REENTRANT + /* Create */ + for(i=0; i +#include +#include +#include + +#ifdef _REENTRANT +#include +#endif + +#define CHF_MODULE_ID 255 +#define CHF_EXTENDED_INFO +#include "Chf.h" + +extern ChfTable message_table[]; +extern size_t message_table_size; + +int main(int argc, char *argv[]) +{ + int st; + const char *msg; + const ChfDescriptor *d, *e; + + puts("test07"); + + /* Initialization */ + if(st = ChfStaticInit(argv[0], CHF_DEFAULT, + message_table, message_table_size, 50, 10, 1)) + exit(st); + + /* ChfGetMessage: + message (CHF_MODULE_ID, 1) exists, (CHF_MODULE_ID, 2) does not + */ + msg = ChfGetMessage(CHF_MODULE_ID, 1, "Default_1"); + if(strcmp(msg, "Set_255,Message_1")) exit(EXIT_FAILURE); + msg = ChfGetMessage(CHF_MODULE_ID, 2, "Default_2"); + if(strcmp(msg, "Default_2")) exit(EXIT_FAILURE); + + /* Generate a condition and check descriptor; this is line 46 */ + ChfCondition 3, CHF_WARNING, 456 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 3 + || d->severity != CHF_WARNING + || d->line_number != 46 + || strcmp(d->file_name, "test07.c") + || strcmp(d->message, "Set_255,Arg_456,Message_3") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Generate another condition and check; this is line 60 */ + ChfCondition 4, CHF_INFO, "arg" ChfEnd; + + if((e = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + e->module_id != CHF_MODULE_ID + || e->condition_code != 4 + || e->severity != CHF_INFO + || e->line_number != 60 + || strcmp(e->file_name, "test07.c") + || strcmp(e->message, "Set_255,Arg_arg,Message_4") + || e->next != d + ) exit(EXIT_FAILURE); + + /* Discard the previous condition group and create a new one */ + ChfDiscard(); + + /* This is line 77 */ + ChfCondition 5, CHF_ERROR, 456, 789 ChfEnd; + + if((d = ChfGetTopCondition()) == NULL) exit(EXIT_FAILURE); + if( + d->module_id != CHF_MODULE_ID + || d->condition_code != 5 + || d->severity != CHF_ERROR + || d->line_number != 77 + || strcmp(d->file_name, "test07.c") + || strcmp(d->message, "Set_255,Arg_456-789,Message_5") + || d->next != NULL + ) exit(EXIT_FAILURE); + + /* Exit Chf */ + ChfExit(); + exit(EXIT_SUCCESS); +} + +ChfTable message_table[] = +{ + { CHF_MODULE_ID, 1, "Set_255,Message_1" }, + { CHF_MODULE_ID, 3, "Set_255,Arg_%d,Message_3" }, + { CHF_MODULE_ID, 4, "Set_255,Arg_%s,Message_4" }, + { CHF_MODULE_ID, 5, "Set_255,Arg_%d-%d,Message_5" } +}; + +size_t message_table_size = sizeof(message_table)/sizeof(message_table[0]); diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000..f988608 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,80 @@ +$Id: INSTALL.txt,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + +Quick build/installation instructions; shell commands are preceded by '$': + + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + + +To build saturn from the source distribution, follow these steps: + +0) Create an empty directory, and unpack the source distribution archive + into it, for example: + + $ mkdir build_dir + $ cd build_dir + $ gunzip < ../saturn_src_V4_1.tar.gz | tar xf - . + + If you are reading this notice, you probably have already done this... + + +1) Generate the platform-specific Makefile: + + $ xmkmf + + On Solaris platforms, if gcc is your default (or only) compiler, + you may need to force its selection, using the following + command instead of xmkmf: + + $ imake -DUseInstalled -DHasGcc -I/usr/openwin/lib/X11/config + + +2) Generate module dependencies: + + $ make depend + + +3) Build saturn: + + $ make + + +4) Build the documentation. + You need a working Texinfo system to do this: + + $ make doc + + +5) Optionally, install saturn systemwide (you need root privileges + to do this): + + $ make install + + +6) Enjoy diff --git a/Imakefile b/Imakefile new file mode 100644 index 0000000..2f8e576 --- /dev/null +++ b/Imakefile @@ -0,0 +1,229 @@ +# .+ +# +# .identifier : $Id: Imakefile,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +# .context : SATURN, Saturn CPU / HP48 emulator +# .title : $RCSfile: Imakefile,v $ +# .kind : Makefile +# .author : Ivan Cibrario B. +# .site : CSTV-CNR +# .creation : * +# .keywords : * +# .description : +# Imakefile. +# .notes : +# $Log: Imakefile,v $ +# Revision 4.1 2000/12/11 09:54:19 cibrario +# Public release. +# +# Revision 3.16 2000/11/21 16:37:20 cibrario +# Ultrix/IRIX support: +# - Propagate CC and CFLAGS to Chf library as fallback values +# - Added -li library for Ultrix +# - Use RM directly if needed macros are not available +# +# Revision 3.14 2000/11/13 10:24:52 cibrario +# Implemented fast load/save; improved keyboard interface emulation at +# high emulated CPU speed: +# +# - New file disk_io_obj.c +# +# Revision 3.13 2000/11/09 11:14:50 cibrario +# Revised to add file selection box GUI element, CPU halt/run +# requests and emulator's extended functions: +# +# - New files: x_func.h, x_func.c, x_func.msf +# +# Revision 3.9 2000/10/24 16:12:55 cibrario +# Added Texinfo documentation targets +# +# Revision 3.8 2000/10/23 13:18:58 cibrario +# Bug fixes: +# - saturn.cat did not contain Chf messages +# - extended 'clean' target to remove saturn.cat as well +# - CatDestination was wrong on Debian GNU/Linux +# +# Revision 3.6 2000/10/02 13:54:04 cibrario +# ROM handling utilities: +# - Added support for stand-alone build of Chf library in its subdirectory +# - Added build commands for 'pack' utility +# +# Revision 3.5 2000/10/02 09:55:10 cibrario +# Linux support: +# - openpty() is in -lutil +# - message catalog must be installed in /usr/lib/locale/C +# +# Revision 3.3 2000/09/26 15:26:07 cibrario +# Revised to implement Flash ROM write access: +# - Added new files flash49.c and flash49.msf +# +# Revision 3.2 2000/09/22 13:21:33 cibrario +# Implemented preliminary support of HP49 hw architecture: +# - added new modules hw_config.c and romram49.c to SRCS list +# +# Revision 2.5 2000/09/14 15:41:16 cibrario +# Added new files serial.c and serial.msf +# +# Revision 2.1 2000/09/08 15:39:34 cibrario +# Added automatic installation of application default file and +# message catalog; removed platform-dependent options. +# +# Revision 1.1 1998/02/19 12:03:28 cibrario +# Initial revision +# +# +# .- + +# +# Stand-alone build of Chf library in its subdirectory +# +# 3.16: Propagate CC and CFLAGS to Chf library as fallback values +# +#define IHaveSubdirs +#define PassCDebugFlags X_CC="$(CC)" X_CFLAGS="$(CFLAGS)" +SUBDIRS = Chf +MakeSubdirs($(SUBDIRS)) + +# +# Programs built by this Imakefile +# +PROGRAMS= saturn pack + +# +# Source/object files +# Each row corresponds to a group of files; each group has its own +# CHF module identifier and message catalog source file. +# +SRCS1= debug.c \ + cpu.c dis.c emulator.c monitor.c \ + modules.c hw_config.c romram.c romram49.c hdw.c keyb.c \ + disk_io.c disk_io_obj.c \ + display.c x11.c \ + serial.c flash49.c x_func.c\ + saturn.c + +# +# Message catalog source files for the modules defined above. +# +MSFS= debug.msf \ + cpu.msf \ + modules.msf \ + disk_io.msf \ + x11.msf \ + serial.msf flash49.msf x_func.msf\ + saturn.msf util.msf \ + Chf/chf.msf + +# +# Source/object files of secondary target +# +SRCS2= pack.c + +# +# Automatic generation of object file list +# +OBJS1= $(SRCS1:.c=.o) +OBJS2= $(SRCS2:.c=.o) disk_io.o debug.o + +# +# Now, some extras... bring the Chf library in. +# +EXTRA_INCLUDES = -I./Chf +EXTRA_LOAD_FLAGS = -L./Chf/st_build + +# 3.5: Added linux support; openpty() is in -lutil +# + +#if defined(LinuxArchitecture) +EXTRA_ARCH_LIBRARIES = -lutil +#endif + +# 3.16: Added Ultrix support; catopen() is in -li +# + +#if defined(UltrixArchitecture) +EXTRA_TAIL_LIBRARIES = -li +#endif + +EXTRA_LIBRARIES = $(EXTRA_ARCH_LIBRARIES) \ +-lXm $(XTOOLLIB) $(XLIB) -lChf \ +$(EXTRA_TAIL_LIBRARIES) + +# +# Main Target +# +ComplexProgramTarget_1(saturn,NullParameter,NullParameter) + +# +# Main Target +# +ComplexProgramTarget_2(pack,NullParameter,NullParameter) + +# +# Installation of the application default file; primary target only. +# +InstallAppDefaults(Saturn) + +# +# Special message catalog targets +# +saturn.cat: $(MSFS) + for msf in $? ; \ + do gencat saturn.cat $$msf ; \ + done + +all:: saturn.cat + +# +# 3.16: Use RM directly if macro is not available +# +#ifdef ImakeConfigRelease +clean:: + RemoveFile(saturn.cat) +#else +clean:: + $(RM) saturn.cat +#endif + +# +# Determine destination of saturn.cat; for now, few architectures are +# supported. +# +#if defined(AlphaArchitecture) +#define CatDestination /usr/lib/nls/msg/C +#elif defined(SunArchitecture) +#define CatDestination /usr/lib/locale/C/LC_MESSAGES +#elif defined(LinuxArchitecture) +#define CatDestination /usr/share/locale/C/LC_MESSAGES +#endif + +#if defined(CatDestination) +InstallNonExecFile(saturn.cat,CatDestination) +#else +install:: + echo "*** Can't determine CatDestination; install saturn.cat manually" +#endif + +# +# TexInfo Documentation +# +MAKEINFO= makeinfo +TEXI2DVI= texi2dvi +DVIPS= dvips + +doc: + $(MAKEINFO) saturn.texi + $(TEXI2DVI) saturn.texi && $(DVIPS) -o saturn.ps saturn.dvi + @echo "*** Install saturn.info manually, please" + +# +# 3.16: Use RM directly if macro is not available +# +#ifdef ImakeConfigRelease +clean:: + RemoveFiles(*.aux *.cp *.cps *.dvi *.fn *.info *.ky *.log *.pg) + RemoveFiles(*.ps *.toc *.tp *.vr) +#else +clean:: + $(RM) *.aux *.cp *.cps *.dvi *.fn *.info *.ky *.log *.pg + $(RM) *.ps *.toc *.tp *.vr +#endif diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..d8649ab --- /dev/null +++ b/README.txt @@ -0,0 +1,49 @@ +$Id: README.txt,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + + +To get started with the saturn software, follow these steps: + +- If you have an user-level binary distribution, read the file + BUILD_INFO.txt and check that the platform on which the + saturn software was built is compatible with yours. + +- If you have a source distribution, follow the instructions + of INSTALL.txt to build the binaries and, optionally, + to install them. + +- Read the documentation of the saturn software, above all chapter 2. + You must build the documentation yourself as explained in INSTALL.txt + if you have a source distribution; instead, if you have a binary + distribution, both info and PostScript files are already there. + +- If you are in a real hurry and wish to emulate an HP49 as soon + as possible, run the ./quick_start shell script. diff --git a/Saturn.ad b/Saturn.ad new file mode 100644 index 0000000..adaa8da --- /dev/null +++ b/Saturn.ad @@ -0,0 +1,2422 @@ +! .+ +! +! .identifier : $Id: Saturn.ad,v 4.1.1.1 2002/11/11 16:13:56 cibrario Exp $ +! .context : SATURN, Saturn CPU / HP48 emulator +! .title : $RCSfile: Saturn.ad,v $ +! .kind : Application's resource file +! .author : Ivan Cibrario B. +! .site : CSTV-CNR +! .creation : 7-Sep-2000 +! .keywords : * +! .description : +! +! .notes : +! $Log: Saturn.ad,v $ +! Revision 4.1.1.1 2002/11/11 16:13:56 cibrario +! Small screen support; preliminary +! +! Revision 4.1 2000/12/11 09:54:19 cibrario +! Public release. +! +! Revision 3.17 2000/11/23 16:56:22 cibrario +! Implemented sutil library and assorted bug fixes: +! - Put leading colons back into translation table entries related to +! main keyboard keys; the match scope was too broad and sometimes +! prevented the use of main keyboard's numeric keys. +! +! Revision 3.16 2000/11/21 16:33:42 cibrario +! Ultrix/IRIX support: +! - Removed all leading colons from translation table entries, to ignore +! standard modifiers when matching KeySyms. +! +! Revision 3.15 2000/11/15 13:59:13 cibrario +! GUI enhancements and assorted bug fixes: +! - Added default resources for new GUI elements +! - Explicit setting of deleteResponse to XmDO_NOTHING for popup shells +! +! Revision 3.14 2000/11/13 10:26:02 cibrario +! Implemented fast load/save; improved keyboard interface emulation at +! high emulated CPU speed: +! +! - Added baseTranslations for fsb widget; needed because default +! Motif VirtualBindings are disabled. +! +! Revision 3.13 2000/11/09 13:21:56 cibrario +! Revised to add file selection box GUI element, CPU halt/run +! requests and emulator's extended functions: +! +! - Added File Selection Box resources +! +! Revision 3.8 2000/10/23 13:10:38 cibrario +! Bug fix: +! Adjusted size of LCD display widget (all faceplates) +! +! Revision 3.7 2000/10/19 14:30:08 cibrario +! Minor updates: +! - disabled traversal on the emulated keyboard area, set default keyboard +! focus policy to XmEXPLICIT. This forces the focus to be on the LCD +! display. +! - resource 'translations' is no longer set here, 'baseTranslations' is +! set instead, so the user can augment/override it. +! - Alt/Shift_L/Right Shift keyboard shortcuts are now enabled on all faces +! - removed spurious (and useless) translation involving the Tab key. +! +! Revision 3.4 2000/09/27 10:11:00 cibrario +! Implemented preliminary support of HP39/40 faceplate; added resources for +! the 'hp40' face. +! +! Revision 3.2 2000/09/22 13:23:30 cibrario +! Implemented preliminary support of HP49 hw architecture: +! - Added resources for the 'hp49' face +! +! Revision 2.8 2000/09/19 12:53:05 cibrario +! Widened the scope of the resource specifier for keyboard shortcuts, +! in order to enable them on the whole surface of the application's +! window. +! +! Revision 2.2 2000/09/11 14:00:26 cibrario +! - Added shortcut for KP_0 key +! - Set default keyboardFocusPolicy to XmPOINTE +! +! Revision 2.1 2000/09/08 15:33:26 cibrario +! *** empty log message *** +! +! .- + +! Suppress all OSF/Motif virtualBindings; they interfer with some (much needed) +! keyboard translations used by this application, notably those involving +! cursor keys. + +*defaultVirtualBindings: + +! The default keyboard focus policy is XmEXPLICIT, but +! the emulated keyboard cannot be traversed; this forces the focus to be +! on the LCD display. There, Motif grabs less tranlations than elsewhere. + +*keyboardFocusPolicy: XmEXPLICIT +*kbd*traversalOn: false + +! Error Dialog resources + +*error_popup*deleteResponse: XmDO_NOTHING +*error*fontList: *helvetica-medium-r-*-*-8-*,*symbol-*-*-*-*-8-*=S +*error*dialogTitle: Attention, please... +*error*okLabelString: Continue + +! Set a bold foreground for the main message window + +*msg*foreground: red +*msg*cursorPositionVisible: False +*msg*fontList: *helvetica-medium-r-*-*-8-*,*symbol-*-*-*-*-8-*=S + +! Destroy application when 'Close' is selected from mwm menu. + +*deleteResponse: XmDESTROY + +! Main window; enable automatic scrolling + +*main.scrollingPolicy: XmAUTOMATIC + +! Declare attachments of keyboard XmForm components, for all faces + +*kbd.XmForm.topAttachment: XmATTACH_POSITION +*kbd.XmForm.bottomAttachment: XmATTACH_POSITION +*kbd.XmForm.leftAttachment: XmATTACH_POSITION +*kbd.XmForm.rightAttachment: XmATTACH_POSITION + +! File Selection Box resources + +*fsb_popup*deleteResponse: XmDO_NOTHING +*fsb*fontList: *helvetica-medium-r-*-*-8-* +*fsb*XmTextField.baseTranslations: #override \n\ +BackSpace: delete-previous-character() \n\ +Delete: delete-next-character() \n\ +Left: backward-character() \n\ +Right: forward-character() + +! ----------------------------------------------------------------------------- +! The following settings act on the hp48 face only + +! Background color + +*hp48*background: gray45 + +! XmRowColumn container settings; vertical layout, packed + +*hp48.numColumns: 1 +*hp48.orientation: XmVERTICAL +*hp48.packing: XmPACK_TIGHT + +! Keyboard layout + +*hp48.nKeys: 49 +*hp48.kbd.fractionBase: 1440 +*hp48.kbd*fontList: *helvetica-*-r-*-*-10-*,*symbol-*-*-*-*-10-*=S +*hp48.kbd*btn.fontList: *helvetica-*-r-*-*-12-*,*symbol-*-*-*-*-12-*=S +*hp48.kbd*ul.foreground: dark violet +*hp48.kbd*ur.foreground: dark green +*hp48.kbd*ll.foreground: gray90 +*hp48.kbd*lr.foreground: gray90 +*hp48.kbd*btn.foreground: wheat +*hp48.kbd*btn.shadowThickness: 1 + +! Lcd screen colors and layout + +*hp48.frame*background: #cadd5c +*hp48.frame*foreground: black +*hp48*lcd.childType: XmFRAME_TITLE_CHILD +*hp48*lcd.childHorizontalAlignment: XmALIGNMENT_CENTER +*hp48*lcd.childVerticalAlignment: XmALIGNMENT_WIDGET_BOTTOM +*hp48*lcd.resizePolicy: XmRESIZE_NONE +*hp48*lcd.width: 272 +*hp48*lcd.height: 156 + +! Suppress all lower-left keyboard labels; the hp48 does not use them + +*hp48.kbd*ll.labelString: + +! Resources for all keyboard keys +! First row + +*hp48.kbd.0.topPosition: 0 +*hp48.kbd.0.bottomPosition: 160 +*hp48.kbd.0.leftPosition: 0 +*hp48.kbd.0.rightPosition: 240 +*hp48.kbd.0.btn.labelString: A +*hp48.kbd.0.ul.labelString: \ +*hp48.kbd.0.ur.labelString: \ +*hp48.kbd.0.lr.labelString: A + +*hp48.kbd.1.topPosition: 0 +*hp48.kbd.1.bottomPosition: 160 +*hp48.kbd.1.leftPosition: 240 +*hp48.kbd.1.rightPosition: 480 +*hp48.kbd.1.btn.labelString: B +*hp48.kbd.1.ul.labelString: \ +*hp48.kbd.1.ur.labelString: \ +*hp48.kbd.1.lr.labelString: B + +*hp48.kbd.2.topPosition: 0 +*hp48.kbd.2.bottomPosition: 160 +*hp48.kbd.2.leftPosition: 480 +*hp48.kbd.2.rightPosition: 720 +*hp48.kbd.2.btn.labelString: C +*hp48.kbd.2.ul.labelString: \ +*hp48.kbd.2.ur.labelString: \ +*hp48.kbd.2.lr.labelString: C + +*hp48.kbd.3.topPosition: 0 +*hp48.kbd.3.bottomPosition: 160 +*hp48.kbd.3.leftPosition: 720 +*hp48.kbd.3.rightPosition: 960 +*hp48.kbd.3.btn.labelString: D +*hp48.kbd.3.ul.labelString: \ +*hp48.kbd.3.ur.labelString: \ +*hp48.kbd.3.lr.labelString: D + +*hp48.kbd.4.topPosition: 0 +*hp48.kbd.4.bottomPosition: 160 +*hp48.kbd.4.leftPosition: 960 +*hp48.kbd.4.rightPosition: 1200 +*hp48.kbd.4.btn.labelString: E +*hp48.kbd.4.ul.labelString: \ +*hp48.kbd.4.ur.labelString: \ +*hp48.kbd.4.lr.labelString: E + +*hp48.kbd.5.topPosition: 0 +*hp48.kbd.5.bottomPosition: 160 +*hp48.kbd.5.leftPosition: 1200 +*hp48.kbd.5.rightPosition: 1440 +*hp48.kbd.5.btn.labelString: F +*hp48.kbd.5.ul.labelString: \ +*hp48.kbd.5.ur.labelString: \ +*hp48.kbd.5.lr.labelString: F + +! Second row + +*hp48.kbd.6.topPosition: 160 +*hp48.kbd.6.bottomPosition: 320 +*hp48.kbd.6.leftPosition: 0 +*hp48.kbd.6.rightPosition: 240 +*hp48.kbd.6.btn.labelString: MTH +*hp48.kbd.6.ul.labelString: RAD +*hp48.kbd.6.ur.labelString: POLAR +*hp48.kbd.6.lr.labelString: G + +*hp48.kbd.7.topPosition: 160 +*hp48.kbd.7.bottomPosition: 320 +*hp48.kbd.7.leftPosition: 240 +*hp48.kbd.7.rightPosition: 480 +*hp48.kbd.7.btn.labelString: PRG +*hp48.kbd.7.ul.labelString: \ +*hp48.kbd.7.ur.labelString: CHARS +*hp48.kbd.7.lr.labelString: H + +*hp48.kbd.8.topPosition: 160 +*hp48.kbd.8.bottomPosition: 320 +*hp48.kbd.8.leftPosition: 480 +*hp48.kbd.8.rightPosition: 720 +*hp48.kbd.8.btn.labelString: CST +*hp48.kbd.8.ul.labelString: \ +*hp48.kbd.8.ur.labelString: MODES +*hp48.kbd.8.lr.labelString: I + +*hp48.kbd.9.topPosition: 160 +*hp48.kbd.9.bottomPosition: 320 +*hp48.kbd.9.leftPosition: 720 +*hp48.kbd.9.rightPosition: 960 +*hp48.kbd.9.btn.labelString: VAR +*hp48.kbd.9.ul.labelString: \ +*hp48.kbd.9.ur.labelString: MEMORY +*hp48.kbd.9.lr.labelString: J + +*hp48.kbd.10.topPosition: 160 +*hp48.kbd.10.bottomPosition: 320 +*hp48.kbd.10.leftPosition: 960 +*hp48.kbd.10.rightPosition: 1200 +*hp48.kbd.10.btn.compoundString: #S\255 +*hp48.kbd.10.ul.labelString: \ +*hp48.kbd.10.ur.labelString: STACK +*hp48.kbd.10.lr.labelString: K + +*hp48.kbd.11.topPosition: 160 +*hp48.kbd.11.bottomPosition: 320 +*hp48.kbd.11.leftPosition: 1200 +*hp48.kbd.11.rightPosition: 1440 +*hp48.kbd.11.btn.labelString: NXT +*hp48.kbd.11.ul.labelString: PREV +*hp48.kbd.11.ur.labelString: MENU +*hp48.kbd.11.lr.labelString: L + +! Third row + +*hp48.kbd.12.topPosition: 320 +*hp48.kbd.12.bottomPosition: 480 +*hp48.kbd.12.leftPosition: 0 +*hp48.kbd.12.rightPosition: 240 +*hp48.kbd.12.btn.labelString: ' +*hp48.kbd.12.ul.labelString: UP +*hp48.kbd.12.ur.labelString: HOME +*hp48.kbd.12.lr.labelString: M + +*hp48.kbd.13.topPosition: 320 +*hp48.kbd.13.bottomPosition: 480 +*hp48.kbd.13.leftPosition: 240 +*hp48.kbd.13.rightPosition: 480 +*hp48.kbd.13.btn.labelString: STO +*hp48.kbd.13.ul.labelString: DEF +*hp48.kbd.13.ur.labelString: RCL +*hp48.kbd.13.lr.labelString: N + +*hp48.kbd.14.topPosition: 320 +*hp48.kbd.14.bottomPosition: 480 +*hp48.kbd.14.leftPosition: 480 +*hp48.kbd.14.rightPosition: 720 +*hp48.kbd.14.btn.labelString: EVAL +*hp48.kbd.14.ul.compoundString: #S\256# NUM +*hp48.kbd.14.ur.labelString: UNDO +*hp48.kbd.14.lr.labelString: O + +*hp48.kbd.15.topPosition: 320 +*hp48.kbd.15.bottomPosition: 480 +*hp48.kbd.15.leftPosition: 720 +*hp48.kbd.15.rightPosition: 960 +*hp48.kbd.15.btn.compoundString: #S\254 +*hp48.kbd.15.ul.labelString: PICTURE +*hp48.kbd.15.ur.labelString: \ +*hp48.kbd.15.lr.labelString: P + +*hp48.kbd.16.topPosition: 320 +*hp48.kbd.16.bottomPosition: 480 +*hp48.kbd.16.leftPosition: 960 +*hp48.kbd.16.rightPosition: 1200 +*hp48.kbd.16.btn.compoundString: #S\257 +*hp48.kbd.16.ul.labelString: VIEW +*hp48.kbd.16.ur.labelString: \ +*hp48.kbd.16.lr.labelString: Q + +*hp48.kbd.17.topPosition: 320 +*hp48.kbd.17.bottomPosition: 480 +*hp48.kbd.17.leftPosition: 1200 +*hp48.kbd.17.rightPosition: 1440 +*hp48.kbd.17.btn.compoundString: #S\256 +*hp48.kbd.17.ul.labelString: SWAP +*hp48.kbd.17.ur.labelString: \ +*hp48.kbd.17.lr.labelString: R + +! Fourth row + +*hp48.kbd.18.topPosition: 480 +*hp48.kbd.18.bottomPosition: 640 +*hp48.kbd.18.leftPosition: 0 +*hp48.kbd.18.rightPosition: 240 +*hp48.kbd.18.btn.labelString: SIN +*hp48.kbd.18.ul.labelString: ASIN +*hp48.kbd.18.ur.compoundString: #S\266 +*hp48.kbd.18.lr.labelString: S + +*hp48.kbd.19.topPosition: 480 +*hp48.kbd.19.bottomPosition: 640 +*hp48.kbd.19.leftPosition: 240 +*hp48.kbd.19.rightPosition: 480 +*hp48.kbd.19.btn.labelString: COS +*hp48.kbd.19.ul.labelString: ACOS +*hp48.kbd.19.ur.compoundString: #S\362 +*hp48.kbd.19.lr.labelString: T + +*hp48.kbd.20.topPosition: 480 +*hp48.kbd.20.bottomPosition: 640 +*hp48.kbd.20.leftPosition: 480 +*hp48.kbd.20.rightPosition: 720 +*hp48.kbd.20.btn.labelString: TAN +*hp48.kbd.20.ul.labelString: ATAN +*hp48.kbd.20.ur.compoundString: #S\123 +*hp48.kbd.20.lr.labelString: U + +*hp48.kbd.21.topPosition: 480 +*hp48.kbd.21.bottomPosition: 640 +*hp48.kbd.21.leftPosition: 720 +*hp48.kbd.21.rightPosition: 960 +*hp48.kbd.21.btn.compoundString: #S\326# x +*hp48.kbd.21.ul.labelString: x^2 +*hp48.kbd.21.ur.compoundString: # x#S\326# y +*hp48.kbd.21.lr.labelString: V + +*hp48.kbd.22.topPosition: 480 +*hp48.kbd.22.bottomPosition: 640 +*hp48.kbd.22.leftPosition: 960 +*hp48.kbd.22.rightPosition: 1200 +*hp48.kbd.22.btn.labelString: y^x +*hp48.kbd.22.ul.labelString: 10^x +*hp48.kbd.22.ur.labelString: LOG +*hp48.kbd.22.lr.labelString: W + +*hp48.kbd.23.topPosition: 480 +*hp48.kbd.23.bottomPosition: 640 +*hp48.kbd.23.leftPosition: 1200 +*hp48.kbd.23.rightPosition: 1440 +*hp48.kbd.23.btn.labelString: 1/x +*hp48.kbd.23.ul.labelString: e^x +*hp48.kbd.23.ur.labelString: LN +*hp48.kbd.23.lr.labelString: X + +! Fifth row + +*hp48.kbd.24.topPosition: 640 +*hp48.kbd.24.bottomPosition: 800 +*hp48.kbd.24.leftPosition: 0 +*hp48.kbd.24.rightPosition: 480 +*hp48.kbd.24.btn.labelString: ENTER +*hp48.kbd.24.ul.labelString: EQUATION +*hp48.kbd.24.ur.labelString: MATRIX +*hp48.kbd.24.lr.labelString: \ + +*hp48.kbd.25.topPosition: 640 +*hp48.kbd.25.bottomPosition: 800 +*hp48.kbd.25.leftPosition: 480 +*hp48.kbd.25.rightPosition: 720 +*hp48.kbd.25.btn.labelString: +/- +*hp48.kbd.25.ul.labelString: EDIT +*hp48.kbd.25.ur.labelString: CMD +*hp48.kbd.25.lr.labelString: Y + +*hp48.kbd.26.topPosition: 640 +*hp48.kbd.26.bottomPosition: 800 +*hp48.kbd.26.leftPosition: 720 +*hp48.kbd.26.rightPosition: 960 +*hp48.kbd.26.btn.labelString: EEX +*hp48.kbd.26.ul.labelString: PURG +*hp48.kbd.26.ur.labelString: ARG +*hp48.kbd.26.lr.labelString: Z + +*hp48.kbd.27.topPosition: 640 +*hp48.kbd.27.bottomPosition: 800 +*hp48.kbd.27.leftPosition: 960 +*hp48.kbd.27.rightPosition: 1200 +*hp48.kbd.27.btn.labelString: DEL +*hp48.kbd.27.ul.labelString: CLEAR +*hp48.kbd.27.ur.labelString: \ +*hp48.kbd.27.lr.labelString: \ + +*hp48.kbd.28.topPosition: 640 +*hp48.kbd.28.bottomPosition: 800 +*hp48.kbd.28.leftPosition: 1200 +*hp48.kbd.28.rightPosition: 1440 +*hp48.kbd.28.btn.compoundString: #S\334 +*hp48.kbd.28.ul.labelString: DROP +*hp48.kbd.28.ur.labelString: \ +*hp48.kbd.28.lr.labelString: \ + +! Sixth row + +*hp48.kbd.29.topPosition: 800 +*hp48.kbd.29.bottomPosition: 960 +*hp48.kbd.29.leftPosition: 0 +*hp48.kbd.29.rightPosition: 240 +*hp48.kbd.29.btn.compoundString: #Sa +*hp48.kbd.29.ul.labelString: USER +*hp48.kbd.29.ur.labelString: ENTRY +*hp48.kbd.29.lr.labelString: \ + +*hp48.kbd.30*background: gray60 +*hp48.kbd.30.topPosition: 800 +*hp48.kbd.30.bottomPosition: 960 +*hp48.kbd.30.leftPosition: 240 +*hp48.kbd.30.rightPosition: 540 +*hp48.kbd.30.btn.labelString: 7 +*hp48.kbd.30.ul.labelString: \ +*hp48.kbd.30.ur.labelString: SOLVE +*hp48.kbd.30.lr.labelString: \ + +*hp48.kbd.31*background: gray60 +*hp48.kbd.31.topPosition: 800 +*hp48.kbd.31.bottomPosition: 960 +*hp48.kbd.31.leftPosition: 540 +*hp48.kbd.31.rightPosition: 840 +*hp48.kbd.31.btn.labelString: 8 +*hp48.kbd.31.ul.labelString: \ +*hp48.kbd.31.ur.labelString: PLOT +*hp48.kbd.31.lr.labelString: \ + +*hp48.kbd.32*background: gray60 +*hp48.kbd.32.topPosition: 800 +*hp48.kbd.32.bottomPosition: 960 +*hp48.kbd.32.leftPosition: 840 +*hp48.kbd.32.rightPosition: 1140 +*hp48.kbd.32.btn.labelString: 9 +*hp48.kbd.32.ul.labelString: \ +*hp48.kbd.32.ur.labelString: SYMBOLIC +*hp48.kbd.32.lr.labelString: \ + +*hp48.kbd.33.topPosition: 800 +*hp48.kbd.33.bottomPosition: 960 +*hp48.kbd.33.leftPosition: 1140 +*hp48.kbd.33.rightPosition: 1440 +*hp48.kbd.33.btn.labelString: / +*hp48.kbd.33.ul.labelString: () +*hp48.kbd.33.ur.labelString: # +*hp48.kbd.33.lr.labelString: \ + +! Seventh row + +*hp48.kbd.34.topPosition: 960 +*hp48.kbd.34.bottomPosition: 1120 +*hp48.kbd.34.leftPosition: 0 +*hp48.kbd.34.rightPosition: 240 +*hp48.kbd.34.btn.background: dark violet +*hp48.kbd.34.btn.foreground: black +*hp48.kbd.34.btn.compoundString: #S\334 +*hp48.kbd.34.ul.labelString: \ +*hp48.kbd.34.ur.labelString: \ +*hp48.kbd.34.lr.labelString: \ + +*hp48.kbd.35*background: gray60 +*hp48.kbd.35.topPosition: 960 +*hp48.kbd.35.bottomPosition: 1120 +*hp48.kbd.35.leftPosition: 240 +*hp48.kbd.35.rightPosition: 540 +*hp48.kbd.35.btn.labelString: 4 +*hp48.kbd.35.ul.labelString: \ +*hp48.kbd.35.ur.labelString: TIME +*hp48.kbd.35.lr.labelString: \ + +*hp48.kbd.36*background: gray60 +*hp48.kbd.36.topPosition: 960 +*hp48.kbd.36.bottomPosition: 1120 +*hp48.kbd.36.leftPosition: 540 +*hp48.kbd.36.rightPosition: 840 +*hp48.kbd.36.btn.labelString: 5 +*hp48.kbd.36.ul.labelString: \ +*hp48.kbd.36.ur.labelString: STAT +*hp48.kbd.36.lr.labelString: \ + +*hp48.kbd.37*background: gray60 +*hp48.kbd.37.topPosition: 960 +*hp48.kbd.37.bottomPosition: 1120 +*hp48.kbd.37.leftPosition: 840 +*hp48.kbd.37.rightPosition: 1140 +*hp48.kbd.37.btn.labelString: 6 +*hp48.kbd.37.ul.labelString: \ +*hp48.kbd.37.ur.labelString: UNITS +*hp48.kbd.37.lr.labelString: \ + +*hp48.kbd.38.topPosition: 960 +*hp48.kbd.38.bottomPosition: 1120 +*hp48.kbd.38.leftPosition: 1140 +*hp48.kbd.38.rightPosition: 1440 +*hp48.kbd.38.btn.compoundString: #S\264 +*hp48.kbd.38.ul.labelString: [] +*hp48.kbd.38.ur.labelString: _ +*hp48.kbd.38.lr.labelString: \ + +! Eigth row + +*hp48.kbd.39.topPosition: 1120 +*hp48.kbd.39.bottomPosition: 1280 +*hp48.kbd.39.leftPosition: 0 +*hp48.kbd.39.rightPosition: 240 +*hp48.kbd.39.btn.background: dark green +*hp48.kbd.39.btn.foreground: black +*hp48.kbd.39.btn.compoundString: #S\336 +*hp48.kbd.39.ul.labelString: \ +*hp48.kbd.39.ur.labelString: \ +*hp48.kbd.39.lr.labelString: \ + +*hp48.kbd.40*background: gray60 +*hp48.kbd.40.topPosition: 1120 +*hp48.kbd.40.bottomPosition: 1280 +*hp48.kbd.40.leftPosition: 240 +*hp48.kbd.40.rightPosition: 540 +*hp48.kbd.40.btn.labelString: 1 +*hp48.kbd.40.ul.labelString: \ +*hp48.kbd.40.ur.labelString: I/O +*hp48.kbd.40.lr.labelString: \ + +*hp48.kbd.41*background: gray60 +*hp48.kbd.41.topPosition: 1120 +*hp48.kbd.41.bottomPosition: 1280 +*hp48.kbd.41.leftPosition: 540 +*hp48.kbd.41.rightPosition: 840 +*hp48.kbd.41.btn.labelString: 2 +*hp48.kbd.41.ul.labelString: \ +*hp48.kbd.41.ur.labelString: LIBRARY +*hp48.kbd.41.lr.labelString: \ + +*hp48.kbd.42*background: gray60 +*hp48.kbd.42.topPosition: 1120 +*hp48.kbd.42.bottomPosition: 1280 +*hp48.kbd.42.leftPosition: 840 +*hp48.kbd.42.rightPosition: 1140 +*hp48.kbd.42.btn.labelString: 3 +*hp48.kbd.42.ul.labelString: \ +*hp48.kbd.42.ur.labelString: EQ LIB +*hp48.kbd.42.lr.labelString: \ + +*hp48.kbd.43.topPosition: 1120 +*hp48.kbd.43.bottomPosition: 1280 +*hp48.kbd.43.leftPosition: 1140 +*hp48.kbd.43.rightPosition: 1440 +*hp48.kbd.43.btn.labelString: - +*hp48.kbd.43.ul.labelString: <<>> +*hp48.kbd.43.ur.labelString: "" +*hp48.kbd.43.lr.labelString: \ + +! Ninth row + +*hp48.kbd.44.topPosition: 1280 +*hp48.kbd.44.bottomPosition: 1440 +*hp48.kbd.44.leftPosition: 0 +*hp48.kbd.44.rightPosition: 240 +*hp48.kbd.44.btn.labelString: ON +*hp48.kbd.44.ul.labelString: CONT +*hp48.kbd.44.ur.labelString: OFF +*hp48.kbd.44.lr.labelString: CANCEL + +*hp48.kbd.45.topPosition: 1280 +*hp48.kbd.45.bottomPosition: 1440 +*hp48.kbd.45.leftPosition: 240 +*hp48.kbd.45.rightPosition: 540 +*hp48.kbd.45.btn.labelString: 0 +*hp48.kbd.45.ul.labelString: = +*hp48.kbd.45.ur.compoundString: #S\256 +*hp48.kbd.45.lr.labelString: \ + +*hp48.kbd.46.topPosition: 1280 +*hp48.kbd.46.bottomPosition: 1440 +*hp48.kbd.46.leftPosition: 540 +*hp48.kbd.46.rightPosition: 840 +*hp48.kbd.46.btn.labelString: . +*hp48.kbd.46.ul.labelString: , +*hp48.kbd.46.ur.compoundString: #S\277 +*hp48.kbd.46.lr.labelString: \ + +*hp48.kbd.47.topPosition: 1280 +*hp48.kbd.47.bottomPosition: 1440 +*hp48.kbd.47.leftPosition: 840 +*hp48.kbd.47.rightPosition: 1140 +*hp48.kbd.47.btn.labelString: SPC +*hp48.kbd.47.ul.compoundString: #Sp +*hp48.kbd.47.ur.compoundString: #S\320 +*hp48.kbd.47.lr.labelString: \ + +*hp48.kbd.48.topPosition: 1280 +*hp48.kbd.48.bottomPosition: 1440 +*hp48.kbd.48.leftPosition: 1140 +*hp48.kbd.48.rightPosition: 1440 +*hp48.kbd.48.btn.labelString: + +*hp48.kbd.48.ul.labelString: {} +*hp48.kbd.48.ur.labelString: :: +*hp48.kbd.48.lr.labelString: \ + +! inOut codes for all keys + +*hp48.kbd.0.btn.inOut: 1/10 +*hp48.kbd.1.btn.inOut: 8/10 +*hp48.kbd.2.btn.inOut: 8/08 +*hp48.kbd.3.btn.inOut: 8/04 +*hp48.kbd.4.btn.inOut: 8/02 +*hp48.kbd.5.btn.inOut: 8/01 +*hp48.kbd.6.btn.inOut: 2/10 +*hp48.kbd.7.btn.inOut: 7/10 +*hp48.kbd.8.btn.inOut: 7/08 +*hp48.kbd.9.btn.inOut: 7/04 +*hp48.kbd.10.btn.inOut: 7/02 +*hp48.kbd.11.btn.inOut: 7/01 +*hp48.kbd.12.btn.inOut: 0/10 +*hp48.kbd.13.btn.inOut: 6/10 +*hp48.kbd.14.btn.inOut: 6/08 +*hp48.kbd.15.btn.inOut: 6/04 +*hp48.kbd.16.btn.inOut: 6/02 +*hp48.kbd.17.btn.inOut: 6/01 +*hp48.kbd.18.btn.inOut: 3/10 +*hp48.kbd.19.btn.inOut: 5/10 +*hp48.kbd.20.btn.inOut: 5/08 +*hp48.kbd.21.btn.inOut: 5/04 +*hp48.kbd.22.btn.inOut: 5/02 +*hp48.kbd.23.btn.inOut: 5/01 +*hp48.kbd.24.btn.inOut: 4/10 +*hp48.kbd.25.btn.inOut: 4/08 +*hp48.kbd.26.btn.inOut: 4/04 +*hp48.kbd.27.btn.inOut: 4/02 +*hp48.kbd.28.btn.inOut: 4/01 +*hp48.kbd.29.btn.inOut: 3/20 +*hp48.kbd.30.btn.inOut: 3/08 +*hp48.kbd.31.btn.inOut: 3/04 +*hp48.kbd.32.btn.inOut: 3/02 +*hp48.kbd.33.btn.inOut: 3/01 +*hp48.kbd.34.btn.inOut: 2/20 +*hp48.kbd.35.btn.inOut: 2/08 +*hp48.kbd.36.btn.inOut: 2/04 +*hp48.kbd.37.btn.inOut: 2/02 +*hp48.kbd.38.btn.inOut: 2/01 +*hp48.kbd.39.btn.inOut: 1/20 +*hp48.kbd.40.btn.inOut: 1/08 +*hp48.kbd.41.btn.inOut: 1/04 +*hp48.kbd.42.btn.inOut: 1/02 +*hp48.kbd.43.btn.inOut: 1/01 +*hp48.kbd.44.btn.inOut: * +*hp48.kbd.45.btn.inOut: 0/08 +*hp48.kbd.46.btn.inOut: 0/04 +*hp48.kbd.47.btn.inOut: 0/02 +*hp48.kbd.48.btn.inOut: 0/01 + +! Shortcut keyboard translations associated with the lcd display widget. +! They must be listed in more-to-less-specific order; so, for example, +! translations involving BackSpace should *before* those involving Delete + +*hp48*baseTranslations: #override \n\ +:apostrophe: kbdKeyPress(0/10) \n\ +:apostrophe: kbdKeyRelease(0/10) \n\ +:BackSpace: kbdKeyPress(4/01) \n\ +:BackSpace: kbdKeyRelease(4/01) \n\ +:Delete: kbdKeyPress(4/02) \n\ +:Delete: kbdKeyRelease(4/02) \n\ +:slash: kbdKeyPress(3/01) \n\ +:slash: kbdKeyRelease(3/01) \n\ +:asterisk: kbdKeyPress(2/01) \n\ +:asterisk: kbdKeyRelease(2/01) \n\ +:minus: kbdKeyPress(1/01) \n\ +:minus: kbdKeyRelease(1/01) \n\ +:period: kbdKeyPress(0/04) \n\ +:period: kbdKeyRelease(0/04) \n\ +:plus: kbdKeyPress(0/01) \n\ +:plus: kbdKeyRelease(0/01) \n\ +F1: kbdKeyPress(1/10) \n\ +F1: kbdKeyRelease(1/10) \n\ +F2: kbdKeyPress(8/10) \n\ +F2: kbdKeyRelease(8/10) \n\ +F3: kbdKeyPress(8/08) \n\ +F3: kbdKeyRelease(8/08) \n\ +F4: kbdKeyPress(8/04) \n\ +F4: kbdKeyRelease(8/04) \n\ +F5: kbdKeyPress(8/02) \n\ +F5: kbdKeyRelease(8/02) \n\ +F6: kbdKeyPress(8/01) \n\ +F6: kbdKeyRelease(8/01) \n\ +A: kbdKeyPress(1/10) \n\ +A: kbdKeyRelease(1/10) \n\ +B: kbdKeyPress(8/10) \n\ +B: kbdKeyRelease(8/10) \n\ +C: kbdKeyPress(8/08) \n\ +C: kbdKeyRelease(8/08) \n\ +D: kbdKeyPress(8/04) \n\ +D: kbdKeyRelease(8/04) \n\ +E: kbdKeyPress(8/02) \n\ +E: kbdKeyRelease(8/02) \n\ +F: kbdKeyPress(8/01) \n\ +F: kbdKeyRelease(8/01) \n\ +G: kbdKeyPress(2/10) \n\ +G: kbdKeyRelease(2/10) \n\ +H: kbdKeyPress(7/10) \n\ +H: kbdKeyRelease(7/10) \n\ +I: kbdKeyPress(7/08) \n\ +I: kbdKeyRelease(7/08) \n\ +J: kbdKeyPress(7/04) \n\ +J: kbdKeyRelease(7/04) \n\ +K: kbdKeyPress(7/02) \n\ +Up: kbdKeyPress(7/02) \n\ +K: kbdKeyRelease(7/02) \n\ +Up: kbdKeyRelease(7/02) \n\ +L: kbdKeyPress(7/01) \n\ +L: kbdKeyRelease(7/01) \n\ +M: kbdKeyPress(0/10) \n\ +M: kbdKeyRelease(0/10) \n\ +N: kbdKeyPress(6/10) \n\ +N: kbdKeyRelease(6/10) \n\ +O: kbdKeyPress(6/08) \n\ +O: kbdKeyRelease(6/08) \n\ +P: kbdKeyPress(6/04) \n\ +Left: kbdKeyPress(6/04) \n\ +P: kbdKeyRelease(6/04) \n\ +Left: kbdKeyRelease(6/04) \n\ +Q: kbdKeyPress(6/02) \n\ +Down: kbdKeyPress(6/02) \n\ +Q: kbdKeyRelease(6/02) \n\ +Down: kbdKeyRelease(6/02) \n\ +R: kbdKeyPress(6/01) \n\ +Right: kbdKeyPress(6/01) \n\ +R: kbdKeyRelease(6/01) \n\ +Right: kbdKeyRelease(6/01) \n\ +S: kbdKeyPress(3/10) \n\ +S: kbdKeyRelease(3/10) \n\ +T: kbdKeyPress(5/10) \n\ +T: kbdKeyRelease(5/10) \n\ +U: kbdKeyPress(5/08) \n\ +U: kbdKeyRelease(5/08) \n\ +V: kbdKeyPress(5/04) \n\ +V: kbdKeyRelease(5/04) \n\ +W: kbdKeyPress(5/02) \n\ +W: kbdKeyRelease(5/02) \n\ +X: kbdKeyPress(5/01) \n\ +X: kbdKeyRelease(5/01) \n\ +Return: kbdKeyPress(4/10) \n\ +Return: kbdKeyRelease(4/10) \n\ +KP_Enter: kbdKeyPress(4/10) \n\ +KP_Enter: kbdKeyRelease(4/10) \n\ +Y: kbdKeyPress(4/08) \n\ +Y: kbdKeyRelease(4/08) \n\ +Z: kbdKeyPress(4/04) \n\ +Z: kbdKeyRelease(4/04) \n\ +F10: kbdKeyPress(3/20) \n\ +F10: kbdKeyRelease(3/20) \n\ +7: kbdKeyPress(3/08) \n\ +7: kbdKeyRelease(3/08) \n\ +KP_7: kbdKeyPress(3/08) \n\ +KP_7: kbdKeyRelease(3/08) \n\ +8: kbdKeyPress(3/04) \n\ +8: kbdKeyRelease(3/04) \n\ +KP_8: kbdKeyPress(3/04) \n\ +KP_8: kbdKeyRelease(3/04) \n\ +9: kbdKeyPress(3/02) \n\ +9: kbdKeyRelease(3/02) \n\ +KP_9: kbdKeyPress(3/02) \n\ +KP_9: kbdKeyRelease(3/02) \n\ +F11: kbdKeyPress(2/20) \n\ +F11: kbdKeyRelease(2/20) \n\ +4: kbdKeyPress(2/08) \n\ +4: kbdKeyRelease(2/08) \n\ +KP_4: kbdKeyPress(2/08) \n\ +KP_4: kbdKeyRelease(2/08) \n\ +5: kbdKeyPress(2/04) \n\ +5: kbdKeyRelease(2/04) \n\ +KP_5: kbdKeyPress(2/04) \n\ +KP_5: kbdKeyRelease(2/04) \n\ +6: kbdKeyPress(2/02) \n\ +6: kbdKeyRelease(2/02) \n\ +KP_6: kbdKeyPress(2/02) \n\ +KP_6: kbdKeyRelease(2/02) \n\ +F12: kbdKeyPress(1/20) \n\ +F12: kbdKeyRelease(1/20) \n\ +1: kbdKeyPress(1/08) \n\ +1: kbdKeyRelease(1/08) \n\ +KP_1: kbdKeyPress(1/08) \n\ +KP_1: kbdKeyRelease(1/08) \n\ +2: kbdKeyPress(1/04) \n\ +2: kbdKeyRelease(1/04) \n\ +KP_2: kbdKeyPress(1/04) \n\ +KP_2: kbdKeyRelease(1/04) \n\ +3: kbdKeyPress(1/02) \n\ +3: kbdKeyRelease(1/02) \n\ +KP_3: kbdKeyPress(1/02) \n\ +KP_3: kbdKeyRelease(1/02) \n\ +Escape: kbdKeyPress(*) \n\ +Escape: kbdKeyRelease(*) \n\ +0: kbdKeyPress(0/08) \n\ +0: kbdKeyRelease(0/08) \n\ +KP_0: kbdKeyPress(0/08) \n\ +KP_0: kbdKeyRelease(0/08) \n\ +space: kbdKeyPress(0/02) \n\ +space: kbdKeyRelease(0/02) \n\ +KP_Add: kbdKeyPress(0/01) \n\ +KP_Add: kbdKeyRelease(0/01) \n\ +KP_Subtract: kbdKeyPress(1/01) \n\ +KP_Subtract: kbdKeyRelease(1/01) \n\ +KP_Multiply: kbdKeyPress(2/01) \n\ +KP_Multiply: kbdKeyRelease(2/01) \n\ +KP_Divide: kbdKeyPress(3/01) \n\ +KP_Divide: kbdKeyRelease(3/01) \n\ +KP_Decimal: kbdKeyPress(0/04) \n\ +KP_Decimal: kbdKeyRelease(0/04) \n\ +Alt_L: kbdKeyPress(3/20) \n\ +Alt_L: kbdKeyRelease(3/20) \n\ +Alt_R: kbdKeyPress(3/20) \n\ +Alt_R: kbdKeyRelease(3/20) \n\ +Shift_L: kbdKeyPress(2/20) \n\ +Shift_L: kbdKeyRelease(2/20) \n\ +Shift_R: kbdKeyPress(1/20) \n\ +Shift_R: kbdKeyRelease(1/20) + + +! ----------------------------------------------------------------------------- +! The following settings act on the hp49 face only +! 4.1.1.1: ... and are an experiment for very small displays + +*hp49*fillOnSelect: true +*hp49*highlightThickness: 0 +*hp49*shadowThickness: 0 + +*hp49*marginWidth: 0 +*hp49.kbd*ul.marginWidth: 1 +*hp49.kbd*lr.marginWidth: 1 +*hp49.kbd*XmForm.marginWidth: 1 + +*hp49*marginHeight: 0 +*hp49*XmToggleButton.marginHeight: -2 +*hp49*XmLabel.marginHeight: -1 +*hp49*msg.marginHeight: 0 + +! Background color + +*hp49*background: light sky blue + +! XmRowColumn container settings; vertical layout, packed + +*hp49.numColumns: 1 +*hp49.orientation: XmVERTICAL +*hp49.packing: XmPACK_TIGHT + +! Keyboard layout + +*hp49.nKeys: 51 +*hp49.kbd.fractionBase: 1124 +*hp49.kbd*fontList: *helvetica-medium-r-*-*-8-*,*symbol-*-*-*-*-8-*=S +*hp49.kbd*btn.fontList: *helvetica-medium-r-*-*-8-*,*symbol-*-*-*-*-8-*=S +*hp49.kbd*ul.foreground: midnight blue +*hp49.kbd*ur.foreground: red +*hp49.kbd*lr.foreground: sea green +*hp49.kbd*btn.background: gray45 +*hp49.kbd*btn.foreground: gray90 +*hp49.kbd*btn.shadowThickness: 0 + +! Lcd screen colors and layout + +*hp49.frame*background: gray20 +*hp49.frame.lcd.background: gray80 +*hp49*lcd.foreground: black +*hp49*lcd.childType: XmFRAME_TITLE_CHILD +*hp49*lcd.childHorizontalAlignment: XmALIGNMENT_CENTER +*hp49*lcd.childVerticalAlignment: XmALIGNMENT_WIDGET_BOTTOM +*hp49*lcd.resizePolicy: XmRESIZE_NONE +*hp49*lcd.width: 133 +*hp49*lcd.height: 80 + +! Suppress all lower-left keyboard labels; the hp49 does not use them + +*hp49.kbd*ll.labelString: + +! Resources for all keyboard keys +! First row + +*hp49.kbd.0.topPosition: 0 +*hp49.kbd.0.bottomPosition: 130 +*hp49.kbd.0.leftPosition: 0 +*hp49.kbd.0.rightPosition: 186 +*hp49.kbd.0.btn.background: gray30 +*hp49.kbd.0.btn.labelString: F1 +*hp49.kbd.0.ul.labelString: Y= +*hp49.kbd.0.ur.labelString: \ +*hp49.kbd.0.lr.labelString: A + +*hp49.kbd.1.topPosition: 0 +*hp49.kbd.1.bottomPosition: 130 +*hp49.kbd.1.leftPosition: 186 +*hp49.kbd.1.rightPosition: 372 +*hp49.kbd.1.btn.background: gray30 +*hp49.kbd.1.btn.labelString: F2 +*hp49.kbd.1.ul.labelString: WIN +*hp49.kbd.1.ur.labelString: \ +*hp49.kbd.1.lr.labelString: B + +*hp49.kbd.2.topPosition: 0 +*hp49.kbd.2.bottomPosition: 130 +*hp49.kbd.2.leftPosition: 372 +*hp49.kbd.2.rightPosition: 558 +*hp49.kbd.2.btn.background: gray30 +*hp49.kbd.2.btn.labelString: F3 +*hp49.kbd.2.ul.labelString: GRAPH +*hp49.kbd.2.ur.labelString: \ +*hp49.kbd.2.lr.labelString: C + +*hp49.kbd.3.topPosition: 0 +*hp49.kbd.3.bottomPosition: 130 +*hp49.kbd.3.leftPosition: 558 +*hp49.kbd.3.rightPosition: 744 +*hp49.kbd.3.btn.background: gray30 +*hp49.kbd.3.btn.labelString: F4 +*hp49.kbd.3.ul.labelString: 2D/3D +*hp49.kbd.3.ur.labelString: \ +*hp49.kbd.3.lr.labelString: D + +*hp49.kbd.4.topPosition: 0 +*hp49.kbd.4.bottomPosition: 130 +*hp49.kbd.4.leftPosition: 744 +*hp49.kbd.4.rightPosition: 930 +*hp49.kbd.4.btn.background: gray30 +*hp49.kbd.4.btn.labelString: F5 +*hp49.kbd.4.ul.labelString: TBLSET +*hp49.kbd.4.ur.labelString: \ +*hp49.kbd.4.lr.labelString: E + +*hp49.kbd.5.topPosition: 0 +*hp49.kbd.5.bottomPosition: 130 +*hp49.kbd.5.leftPosition: 930 +*hp49.kbd.5.rightPosition: 1116 +*hp49.kbd.5.btn.background: gray30 +*hp49.kbd.5.btn.labelString: F6 +*hp49.kbd.5.ul.labelString: TABLE +*hp49.kbd.5.ur.labelString: \ +*hp49.kbd.5.lr.labelString: F + +! Second row/third row + +*hp49.kbd.6.topPosition: 130 +*hp49.kbd.6.bottomPosition: 260 +*hp49.kbd.6.leftPosition: 0 +*hp49.kbd.6.rightPosition: 223 +*hp49.kbd.6.btn.labelString: APPS +*hp49.kbd.6.ul.labelString: FLS +*hp49.kbd.6.ur.labelString: BEG +*hp49.kbd.6.lr.labelString: G + +*hp49.kbd.7.topPosition: 130 +*hp49.kbd.7.bottomPosition: 260 +*hp49.kbd.7.leftPosition: 223 +*hp49.kbd.7.rightPosition: 446 +*hp49.kbd.7.btn.labelString: MODE +*hp49.kbd.7.ul.labelString: CST +*hp49.kbd.7.ur.labelString: END +*hp49.kbd.7.lr.labelString: H + +*hp49.kbd.8.topPosition: 130 +*hp49.kbd.8.bottomPosition: 260 +*hp49.kbd.8.leftPosition: 446 +*hp49.kbd.8.rightPosition: 669 +*hp49.kbd.8.btn.labelString: TOOL +*hp49.kbd.8.ul.labelString: i +*hp49.kbd.8.ur.labelString: | +*hp49.kbd.8.lr.labelString: I + +*hp49.kbd.9.topPosition: 260 +*hp49.kbd.9.bottomPosition: 390 +*hp49.kbd.9.leftPosition: 0 +*hp49.kbd.9.rightPosition: 223 +*hp49.kbd.9.btn.labelString: VAR +*hp49.kbd.9.ul.labelString: UP +*hp49.kbd.9.ur.labelString: CPY +*hp49.kbd.9.lr.labelString: J + +*hp49.kbd.10.topPosition: 260 +*hp49.kbd.10.bottomPosition: 390 +*hp49.kbd.10.leftPosition: 223 +*hp49.kbd.10.rightPosition: 446 +*hp49.kbd.10.btn.compoundString: STO#S\336 +*hp49.kbd.10.ul.labelString: RCL +*hp49.kbd.10.ur.labelString: CUT +*hp49.kbd.10.lr.labelString: K + +*hp49.kbd.11.topPosition: 260 +*hp49.kbd.11.bottomPosition: 390 +*hp49.kbd.11.leftPosition: 446 +*hp49.kbd.11.rightPosition: 669 +*hp49.kbd.11.btn.labelString: NXT +*hp49.kbd.11.ul.labelString: PRV +*hp49.kbd.11.ur.labelString: PST +*hp49.kbd.11.lr.labelString: L + +! Cursor keys + +*hp49.kbd.12.topPosition: 130 +*hp49.kbd.12.bottomPosition: 217 +*hp49.kbd.12.leftPosition: 780 +*hp49.kbd.12.rightPosition: 1003 +*hp49.kbd.12.btn.compoundString: #S\255 +*hp49.kbd.12.ul.labelString: +*hp49.kbd.12.ur.labelString: +*hp49.kbd.12.lr.labelString: +*hp49*kbd.12*marginHeight: 0 + +*hp49.kbd.13.topPosition: 217 +*hp49.kbd.13.bottomPosition: 303 +*hp49.kbd.13.leftPosition: 669 +*hp49.kbd.13.rightPosition: 892 +*hp49.kbd.13.btn.compoundString: #S\254 +*hp49.kbd.13.ul.labelString: +*hp49.kbd.13.ur.labelString: +*hp49.kbd.13.lr.labelString: +*hp49*kbd.13*marginHeight: 0 + +*hp49.kbd.14.topPosition: 217 +*hp49.kbd.14.bottomPosition: 303 +*hp49.kbd.14.leftPosition: 892 +*hp49.kbd.14.rightPosition: 1115 +*hp49.kbd.14.btn.compoundString: #S\256 +*hp49.kbd.14.ul.labelString: +*hp49.kbd.14.ur.labelString: +*hp49.kbd.14.lr.labelString: +*hp49*kbd.14*marginHeight: 0 + +*hp49.kbd.15.topPosition: 303 +*hp49.kbd.15.bottomPosition: 390 +*hp49.kbd.15.leftPosition: 780 +*hp49.kbd.15.rightPosition: 1003 +*hp49.kbd.15.btn.compoundString: #S\257 +*hp49.kbd.15.ul.labelString: +*hp49.kbd.15.ur.labelString: +*hp49.kbd.15.lr.labelString: +*hp49*kbd.15*marginHeight: 0 + +! Fourth row + +*hp49.kbd.16.topPosition: 390 +*hp49.kbd.16.bottomPosition: 520 +*hp49.kbd.16.leftPosition: 0 +*hp49.kbd.16.rightPosition: 223 +*hp49.kbd.16.btn.labelString: HIST +*hp49.kbd.16.ul.labelString: CMD +*hp49.kbd.16.ur.labelString: UND +*hp49.kbd.16.lr.labelString: M + +*hp49.kbd.17.topPosition: 390 +*hp49.kbd.17.bottomPosition: 520 +*hp49.kbd.17.leftPosition: 223 +*hp49.kbd.17.rightPosition: 446 +*hp49.kbd.17.btn.labelString: CAT +*hp49.kbd.17.ul.labelString: PRG +*hp49.kbd.17.ur.labelString: CHR +*hp49.kbd.17.lr.labelString: N + +*hp49.kbd.18.topPosition: 390 +*hp49.kbd.18.bottomPosition: 520 +*hp49.kbd.18.leftPosition: 446 +*hp49.kbd.18.rightPosition: 669 +*hp49.kbd.18.btn.labelString: EQW +*hp49.kbd.18.ul.labelString: MTW +*hp49.kbd.18.ur.labelString: ' +*hp49.kbd.18.lr.labelString: O + +*hp49.kbd.19.topPosition: 390 +*hp49.kbd.19.bottomPosition: 520 +*hp49.kbd.19.leftPosition: 669 +*hp49.kbd.19.rightPosition: 892 +*hp49.kbd.19.btn.labelString: SYMB +*hp49.kbd.19.ul.labelString: MTH +*hp49.kbd.19.ur.labelString: EVL +*hp49.kbd.19.lr.labelString: P + +*hp49.kbd.20.topPosition: 390 +*hp49.kbd.20.bottomPosition: 520 +*hp49.kbd.20.leftPosition: 892 +*hp49.kbd.20.rightPosition: 1115 +*hp49.kbd.20.btn.compoundString: #S\334 +*hp49.kbd.20.ul.labelString: DEL +*hp49.kbd.20.ur.labelString: CLR +*hp49.kbd.20.lr.labelString: \ + +! Fifth row + +*hp49.kbd.21.topPosition: 520 +*hp49.kbd.21.bottomPosition: 650 +*hp49.kbd.21.leftPosition: 0 +*hp49.kbd.21.rightPosition: 223 +*hp49.kbd.21.btn.labelString: Y^X +*hp49.kbd.21.ul.labelString: e^x +*hp49.kbd.21.ur.labelString: LN +*hp49.kbd.21.lr.labelString: Q + +*hp49.kbd.22.topPosition: 520 +*hp49.kbd.22.bottomPosition: 650 +*hp49.kbd.22.leftPosition: 223 +*hp49.kbd.22.rightPosition: 446 +*hp49.kbd.22.btn.compoundString: #S\326# X +*hp49.kbd.22.ul.labelString: x^2 +*hp49.kbd.22.ur.compoundString: x#S\326# y +*hp49.kbd.22.lr.labelString: R + +*hp49.kbd.23.topPosition: 520 +*hp49.kbd.23.bottomPosition: 650 +*hp49.kbd.23.leftPosition: 446 +*hp49.kbd.23.rightPosition: 669 +*hp49.kbd.23.btn.labelString: SIN +*hp49.kbd.23.ul.labelString: ASN +*hp49.kbd.23.ur.compoundString: #SS +*hp49.kbd.23.lr.labelString: S + +*hp49.kbd.24.topPosition: 520 +*hp49.kbd.24.bottomPosition: 650 +*hp49.kbd.24.leftPosition: 669 +*hp49.kbd.24.rightPosition: 892 +*hp49.kbd.24.btn.labelString: COS +*hp49.kbd.24.ul.labelString: ACS +*hp49.kbd.24.ur.compoundString: #S\266 +*hp49.kbd.24.lr.labelString: T + +*hp49.kbd.25.topPosition: 520 +*hp49.kbd.25.bottomPosition: 650 +*hp49.kbd.25.leftPosition: 892 +*hp49.kbd.25.rightPosition: 1115 +*hp49.kbd.25.btn.labelString: TAN +*hp49.kbd.25.ul.labelString: ATN +*hp49.kbd.25.ur.compoundString: #S\362 +*hp49.kbd.25.lr.labelString: U + +! Sixth row + +*hp49.kbd.26.topPosition: 650 +*hp49.kbd.26.bottomPosition: 780 +*hp49.kbd.26.leftPosition: 0 +*hp49.kbd.26.rightPosition: 223 +*hp49.kbd.26.btn.labelString: EEX +*hp49.kbd.26.ul.labelString: 10^x +*hp49.kbd.26.ur.labelString: LOG +*hp49.kbd.26.lr.labelString: V + +*hp49.kbd.27.topPosition: 650 +*hp49.kbd.27.bottomPosition: 780 +*hp49.kbd.27.leftPosition: 223 +*hp49.kbd.27.rightPosition: 446 +*hp49.kbd.27.btn.labelString: +/- +*hp49.kbd.27.ul.compoundString: #S\271 +*hp49.kbd.27.ur.compoundString: #S= +*hp49.kbd.27.lr.labelString: W + +*hp49.kbd.28.topPosition: 650 +*hp49.kbd.28.bottomPosition: 780 +*hp49.kbd.28.leftPosition: 446 +*hp49.kbd.28.rightPosition: 669 +*hp49.kbd.28.btn.labelString: X +*hp49.kbd.28.ul.compoundString: #S\243 +*hp49.kbd.28.ur.compoundString: #S< +*hp49.kbd.28.lr.labelString: X + +*hp49.kbd.29.topPosition: 650 +*hp49.kbd.29.bottomPosition: 780 +*hp49.kbd.29.leftPosition: 669 +*hp49.kbd.29.rightPosition: 892 +*hp49.kbd.29.btn.labelString: 1/X +*hp49.kbd.29.ul.compoundString: #S\263 +*hp49.kbd.29.ur.compoundString: #S> +*hp49.kbd.29.lr.labelString: Y + +*hp49.kbd.30.topPosition: 650 +*hp49.kbd.30.bottomPosition: 780 +*hp49.kbd.30.leftPosition: 892 +*hp49.kbd.30.rightPosition: 1115 +*hp49.kbd.30.btn.background: gray30 +*hp49.kbd.30.btn.compoundString: #S\270 +*hp49.kbd.30.ul.labelString: ABS +*hp49.kbd.30.ur.compoundString: ARG +*hp49.kbd.30.lr.labelString: Z + +! Seventh row + +*hp49.kbd.31.topPosition: 780 +*hp49.kbd.31.bottomPosition: 866 +*hp49.kbd.31.leftPosition: 0 +*hp49.kbd.31.rightPosition: 223 +*hp49.kbd.31.btn.background: sea green +*hp49.kbd.31.btn.labelString: ALPHA +*hp49.kbd.31.ul.labelString: USR +*hp49.kbd.31.ur.labelString: ENT +*hp49.kbd.31.lr.labelString: + +*hp49.kbd.32.topPosition: 780 +*hp49.kbd.32.bottomPosition: 866 +*hp49.kbd.32.leftPosition: 223 +*hp49.kbd.32.rightPosition: 446 +*hp49.kbd.32.btn.background: gray20 +*hp49.kbd.32.btn.labelString: 7 +*hp49.kbd.32.ul.labelString: S.S +*hp49.kbd.32.ur.labelString: N.S +*hp49.kbd.32.lr.labelString: + +*hp49.kbd.33.topPosition: 780 +*hp49.kbd.33.bottomPosition: 866 +*hp49.kbd.33.leftPosition: 446 +*hp49.kbd.33.rightPosition: 669 +*hp49.kbd.33.btn.background: gray20 +*hp49.kbd.33.btn.labelString: 8 +*hp49.kbd.33.ul.labelString: E&L +*hp49.kbd.33.ur.labelString: TRG +*hp49.kbd.33.lr.labelString: + +*hp49.kbd.34.topPosition: 780 +*hp49.kbd.34.bottomPosition: 866 +*hp49.kbd.34.leftPosition: 669 +*hp49.kbd.34.rightPosition: 892 +*hp49.kbd.34.btn.background: gray20 +*hp49.kbd.34.btn.labelString: 9 +*hp49.kbd.34.ul.labelString: FIN +*hp49.kbd.34.ur.labelString: TIM +*hp49.kbd.34.lr.labelString: + +*hp49.kbd.35.topPosition: 780 +*hp49.kbd.35.bottomPosition: 866 +*hp49.kbd.35.leftPosition: 892 +*hp49.kbd.35.rightPosition: 1115 +*hp49.kbd.35.btn.background: gray30 +*hp49.kbd.35.btn.compoundString: #S\264 +*hp49.kbd.35.ul.labelString: [] +*hp49.kbd.35.ur.labelString: "" +*hp49.kbd.35.lr.labelString: + +! Eigth row + +*hp49.kbd.36.topPosition: 866 +*hp49.kbd.36.bottomPosition: 952 +*hp49.kbd.36.leftPosition: 0 +*hp49.kbd.36.rightPosition: 223 +*hp49.kbd.36.btn.background: midnight blue +*hp49.kbd.36.btn.foreground: gray90 +*hp49.kbd.36.btn.compoundString: #S\334 +*hp49.kbd.36.ul.labelString: \ +*hp49.kbd.36.ur.labelString: \ +*hp49.kbd.36.lr.labelString: + +*hp49.kbd.37.topPosition: 866 +*hp49.kbd.37.bottomPosition: 952 +*hp49.kbd.37.leftPosition: 223 +*hp49.kbd.37.rightPosition: 446 +*hp49.kbd.37.btn.background: gray20 +*hp49.kbd.37.btn.labelString: 4 +*hp49.kbd.37.ul.labelString: CAL +*hp49.kbd.37.ur.labelString: ALG +*hp49.kbd.37.lr.labelString: + +*hp49.kbd.38.topPosition: 866 +*hp49.kbd.38.bottomPosition: 952 +*hp49.kbd.38.leftPosition: 446 +*hp49.kbd.38.rightPosition: 669 +*hp49.kbd.38.btn.background: gray20 +*hp49.kbd.38.btn.labelString: 5 +*hp49.kbd.38.ul.labelString: MAT +*hp49.kbd.38.ur.labelString: ST +*hp49.kbd.38.lr.labelString: + +*hp49.kbd.39.topPosition: 866 +*hp49.kbd.39.bottomPosition: 952 +*hp49.kbd.39.leftPosition: 669 +*hp49.kbd.39.rightPosition: 892 +*hp49.kbd.39.btn.background: gray20 +*hp49.kbd.39.btn.labelString: 6 +*hp49.kbd.39.ul.labelString: CVT +*hp49.kbd.39.ur.labelString: UNT +*hp49.kbd.39.lr.labelString: + +*hp49.kbd.40.topPosition: 866 +*hp49.kbd.40.bottomPosition: 952 +*hp49.kbd.40.leftPosition: 892 +*hp49.kbd.40.rightPosition: 1115 +*hp49.kbd.40.btn.background: gray30 +*hp49.kbd.40.btn.labelString: - +*hp49.kbd.40.ul.labelString: () +*hp49.kbd.40.ur.labelString: _ +*hp49.kbd.40.lr.labelString: + +! Ninth row + +*hp49.kbd.41.topPosition: 952 +*hp49.kbd.41.bottomPosition: 1038 +*hp49.kbd.41.leftPosition: 0 +*hp49.kbd.41.rightPosition: 223 +*hp49.kbd.41.btn.background: red +*hp49.kbd.41.btn.foreground: gray90 +*hp49.kbd.41.btn.compoundString: #S\336 +*hp49.kbd.41.ul.labelString: \ +*hp49.kbd.41.ur.labelString: \ +*hp49.kbd.41.lr.labelString: + +*hp49.kbd.42.topPosition: 952 +*hp49.kbd.42.bottomPosition: 1038 +*hp49.kbd.42.leftPosition: 223 +*hp49.kbd.42.rightPosition: 446 +*hp49.kbd.42.btn.background: gray20 +*hp49.kbd.42.btn.labelString: 1 +*hp49.kbd.42.ul.labelString: ART +*hp49.kbd.42.ur.labelString: CPX +*hp49.kbd.42.lr.labelString: + +*hp49.kbd.43.topPosition: 952 +*hp49.kbd.43.bottomPosition: 1038 +*hp49.kbd.43.leftPosition: 446 +*hp49.kbd.43.rightPosition: 669 +*hp49.kbd.43.btn.background: gray20 +*hp49.kbd.43.btn.labelString: 2 +*hp49.kbd.43.ul.labelString: DEF +*hp49.kbd.43.ur.labelString: LIB +*hp49.kbd.43.lr.labelString: + +*hp49.kbd.44.topPosition: 952 +*hp49.kbd.44.bottomPosition: 1038 +*hp49.kbd.44.leftPosition: 669 +*hp49.kbd.44.rightPosition: 892 +*hp49.kbd.44.btn.background: gray20 +*hp49.kbd.44.btn.labelString: 3 +*hp49.kbd.44.ul.labelString: # +*hp49.kbd.44.ur.labelString: BASE +*hp49.kbd.44.lr.labelString: + +*hp49.kbd.45.topPosition: 952 +*hp49.kbd.45.bottomPosition: 1038 +*hp49.kbd.45.leftPosition: 892 +*hp49.kbd.45.rightPosition: 1115 +*hp49.kbd.45.btn.background: gray30 +*hp49.kbd.45.btn.labelString: + +*hp49.kbd.45.ul.labelString: {} +*hp49.kbd.45.ur.labelString: <<>> +*hp49.kbd.45.lr.labelString: + +! Tenth row + +*hp49.kbd.46.topPosition: 1038 +*hp49.kbd.46.bottomPosition: 1124 +*hp49.kbd.46.leftPosition: 0 +*hp49.kbd.46.rightPosition: 223 +*hp49.kbd.46.btn.labelString: ON +*hp49.kbd.46.ul.labelString: CONT +*hp49.kbd.46.ur.labelString: OFF +*hp49.kbd.46.lr.labelString: + +*hp49.kbd.47.topPosition: 1038 +*hp49.kbd.47.bottomPosition: 1124 +*hp49.kbd.47.leftPosition: 223 +*hp49.kbd.47.rightPosition: 446 +*hp49.kbd.47.btn.background: gray20 +*hp49.kbd.47.btn.labelString: 0 +*hp49.kbd.47.ul.compoundString: #S\245 +*hp49.kbd.47.ur.compoundString: #S\256 +*hp49.kbd.47.lr.labelString: + +*hp49.kbd.48.topPosition: 1038 +*hp49.kbd.48.bottomPosition: 1124 +*hp49.kbd.48.leftPosition: 446 +*hp49.kbd.48.rightPosition: 669 +*hp49.kbd.48.btn.background: gray20 +*hp49.kbd.48.btn.labelString: . +*hp49.kbd.48.ul.labelString: :: +*hp49.kbd.48.ur.compoundString: #S\277 +*hp49.kbd.48.lr.labelString: + +*hp49.kbd.49.topPosition: 1038 +*hp49.kbd.49.bottomPosition: 1124 +*hp49.kbd.49.leftPosition: 669 +*hp49.kbd.49.rightPosition: 892 +*hp49.kbd.49.btn.background: gray20 +*hp49.kbd.49.btn.labelString: SPC +*hp49.kbd.49.ul.compoundString: #Sp +*hp49.kbd.49.ur.labelString: , +*hp49.kbd.49.lr.labelString: + +*hp49.kbd.50.topPosition: 1038 +*hp49.kbd.50.bottomPosition: 1124 +*hp49.kbd.50.leftPosition: 892 +*hp49.kbd.50.rightPosition: 1115 +*hp49.kbd.50.btn.background: gray30 +*hp49.kbd.50.btn.labelString: ENTER +*hp49.kbd.50.ul.labelString: ANS +*hp49.kbd.50.ur.compoundString: #S\256# N +*hp49.kbd.50.lr.labelString: + +! inOut codes for all keys + +*hp49.kbd.0.btn.inOut: 5/01 +*hp49.kbd.1.btn.inOut: 5/02 +*hp49.kbd.2.btn.inOut: 5/04 +*hp49.kbd.3.btn.inOut: 5/08 +*hp49.kbd.4.btn.inOut: 5/10 +*hp49.kbd.5.btn.inOut: 5/20 + +*hp49.kbd.6.btn.inOut: 5/80 +*hp49.kbd.7.btn.inOut: 4/80 +*hp49.kbd.8.btn.inOut: 3/80 + +*hp49.kbd.9.btn.inOut: 2/80 +*hp49.kbd.10.btn.inOut: 1/80 +*hp49.kbd.11.btn.inOut: 0/80 + +*hp49.kbd.12.btn.inOut: 6/08 +*hp49.kbd.13.btn.inOut: 6/04 +*hp49.kbd.14.btn.inOut: 6/01 +*hp49.kbd.15.btn.inOut: 6/02 + +*hp49.kbd.16.btn.inOut: 4/40 +*hp49.kbd.17.btn.inOut: 3/40 +*hp49.kbd.18.btn.inOut: 2/40 +*hp49.kbd.19.btn.inOut: 1/40 +*hp49.kbd.20.btn.inOut: 0/40 + +*hp49.kbd.21.btn.inOut: 4/20 +*hp49.kbd.22.btn.inOut: 3/20 +*hp49.kbd.23.btn.inOut: 2/20 +*hp49.kbd.24.btn.inOut: 1/20 +*hp49.kbd.25.btn.inOut: 0/20 + +*hp49.kbd.26.btn.inOut: 4/10 +*hp49.kbd.27.btn.inOut: 3/10 +*hp49.kbd.28.btn.inOut: 2/10 +*hp49.kbd.29.btn.inOut: 1/10 +*hp49.kbd.30.btn.inOut: 0/10 + +*hp49.kbd.31.btn.inOut: 7/08 +*hp49.kbd.32.btn.inOut: 3/08 +*hp49.kbd.33.btn.inOut: 2/08 +*hp49.kbd.34.btn.inOut: 1/08 +*hp49.kbd.35.btn.inOut: 0/08 + +*hp49.kbd.36.btn.inOut: 7/04 +*hp49.kbd.37.btn.inOut: 3/04 +*hp49.kbd.38.btn.inOut: 2/04 +*hp49.kbd.39.btn.inOut: 1/04 +*hp49.kbd.40.btn.inOut: 0/04 + +*hp49.kbd.41.btn.inOut: 7/02 +*hp49.kbd.42.btn.inOut: 3/02 +*hp49.kbd.43.btn.inOut: 2/02 +*hp49.kbd.44.btn.inOut: 1/02 +*hp49.kbd.45.btn.inOut: 0/02 + +*hp49.kbd.46.btn.inOut: * +*hp49.kbd.47.btn.inOut: 3/01 +*hp49.kbd.48.btn.inOut: 2/01 +*hp49.kbd.49.btn.inOut: 1/01 +*hp49.kbd.50.btn.inOut: 0/01 + +! Shortcut keyboard translations associated with the lcd display widget. +! They must be listed in more-to-less-specific order; so, for example, +! translations involving BackSpace should *before* those involving Delete + +*hp49*baseTranslations: #override \n\ +F1: kbdKeyPress(5/01) \n\ +F1: kbdKeyRelease(5/01) \n\ +F2: kbdKeyPress(5/02) \n\ +F2: kbdKeyRelease(5/02) \n\ +F3: kbdKeyPress(5/04) \n\ +F3: kbdKeyRelease(5/04) \n\ +F4: kbdKeyPress(5/08) \n\ +F4: kbdKeyRelease(5/08) \n\ +F5: kbdKeyPress(5/10) \n\ +F5: kbdKeyRelease(5/10) \n\ +F6: kbdKeyPress(5/20) \n\ +F6: kbdKeyRelease(5/20) \n\ +A: kbdKeyPress(5/01) \n\ +A: kbdKeyRelease(5/01) \n\ +B: kbdKeyPress(5/02) \n\ +B: kbdKeyRelease(5/02) \n\ +C: kbdKeyPress(5/04) \n\ +C: kbdKeyRelease(5/04) \n\ +D: kbdKeyPress(5/08) \n\ +D: kbdKeyRelease(5/08) \n\ +E: kbdKeyPress(5/10) \n\ +E: kbdKeyRelease(5/10) \n\ +F: kbdKeyPress(5/20) \n\ +F: kbdKeyRelease(5/20) \n\ +G: kbdKeyPress(5/80) \n\ +G: kbdKeyRelease(5/80) \n\ +H: kbdKeyPress(4/80) \n\ +H: kbdKeyRelease(4/80) \n\ +I: kbdKeyPress(3/80) \n\ +I: kbdKeyRelease(3/80) \n\ +J: kbdKeyPress(2/80) \n\ +J: kbdKeyRelease(2/80) \n\ +K: kbdKeyPress(1/80) \n\ +K: kbdKeyRelease(1/80) \n\ +L: kbdKeyPress(0/80) \n\ +L: kbdKeyRelease(0/80) \n\ +Up: kbdKeyPress(6/08) \n\ +Up: kbdKeyRelease(6/08) \n\ +Left: kbdKeyPress(6/04) \n\ +Left: kbdKeyRelease(6/04) \n\ +Right: kbdKeyPress(6/01) \n\ +Right: kbdKeyRelease(6/01) \n\ +Down: kbdKeyPress(6/02) \n\ +Down: kbdKeyRelease(6/02) \n\ +M: kbdKeyPress(4/40) \n\ +M: kbdKeyRelease(4/40) \n\ +N: kbdKeyPress(3/40) \n\ +N: kbdKeyRelease(3/40) \n\ +O: kbdKeyPress(2/40) \n\ +O: kbdKeyRelease(2/40) \n\ +P: kbdKeyPress(1/40) \n\ +P: kbdKeyRelease(1/40) \n\ +BackSpace: kbdKeyPress(0/40) \n\ +BackSpace: kbdKeyRelease(0/40) \n\ +Q: kbdKeyPress(4/20) \n\ +Q: kbdKeyRelease(4/20) \n\ +R: kbdKeyPress(3/20) \n\ +R: kbdKeyRelease(3/20) \n\ +S: kbdKeyPress(2/20) \n\ +S: kbdKeyRelease(2/20) \n\ +T: kbdKeyPress(1/20) \n\ +T: kbdKeyRelease(1/20) \n\ +U: kbdKeyPress(0/20) \n\ +U: kbdKeyRelease(0/20) \n\ +V: kbdKeyPress(4/10) \n\ +V: kbdKeyRelease(4/10) \n\ +W: kbdKeyPress(3/10) \n\ +W: kbdKeyRelease(3/10) \n\ +X: kbdKeyPress(2/10) \n\ +X: kbdKeyRelease(2/10) \n\ +Y: kbdKeyPress(1/10) \n\ +Y: kbdKeyRelease(1/10) \n\ +Z: kbdKeyPress(0/10) \n\ +Z: kbdKeyRelease(0/10) \n\ +:slash: kbdKeyPress(0/10) \n\ +:slash: kbdKeyRelease(0/10) \n\ +KP_Divide: kbdKeyPress(0/10) \n\ +KP_Divide: kbdKeyRelease(0/10) \n\ +F10: kbdKeyPress(7/08) \n\ +F10: kbdKeyRelease(7/08) \n\ +7: kbdKeyPress(3/08) \n\ +7: kbdKeyRelease(3/08) \n\ +KP_7: kbdKeyPress(3/08) \n\ +KP_7: kbdKeyRelease(3/08) \n\ +8: kbdKeyPress(2/08) \n\ +8: kbdKeyRelease(2/08) \n\ +KP_8: kbdKeyPress(2/08) \n\ +KP_8: kbdKeyRelease(2/08) \n\ +9: kbdKeyPress(1/08) \n\ +9: kbdKeyRelease(1/08) \n\ +KP_9: kbdKeyPress(1/08) \n\ +KP_9: kbdKeyRelease(1/08) \n\ +:asterisk: kbdKeyPress(0/08) \n\ +:asterisk: kbdKeyRelease(0/08) \n\ +KP_Multiply: kbdKeyPress(0/08) \n\ +KP_Multiply: kbdKeyRelease(0/08) \n\ +F11: kbdKeyPress(7/04) \n\ +F11: kbdKeyRelease(7/04) \n\ +4: kbdKeyPress(3/04) \n\ +4: kbdKeyRelease(3/04) \n\ +KP_4: kbdKeyPress(3/04) \n\ +KP_4: kbdKeyRelease(3/04) \n\ +5: kbdKeyPress(2/04) \n\ +5: kbdKeyRelease(2/04) \n\ +KP_5: kbdKeyPress(2/04) \n\ +KP_5: kbdKeyRelease(2/04) \n\ +6: kbdKeyPress(1/04) \n\ +6: kbdKeyRelease(1/04) \n\ +KP_6: kbdKeyPress(1/04) \n\ +KP_6: kbdKeyRelease(1/04) \n\ +:minus: kbdKeyPress(0/04) \n\ +:minus: kbdKeyRelease(0/04) \n\ +KP_Subtract: kbdKeyPress(0/04) \n\ +KP_Subtract: kbdKeyRelease(0/04) \n\ +F12: kbdKeyPress(7/02) \n\ +F12: kbdKeyRelease(7/02) \n\ +1: kbdKeyPress(3/02) \n\ +1: kbdKeyRelease(3/02) \n\ +KP_1: kbdKeyPress(3/02) \n\ +KP_1: kbdKeyRelease(3/02) \n\ +2: kbdKeyPress(2/02) \n\ +2: kbdKeyRelease(2/02) \n\ +KP_2: kbdKeyPress(2/02) \n\ +KP_2: kbdKeyRelease(2/02) \n\ +3: kbdKeyPress(1/02) \n\ +3: kbdKeyRelease(1/02) \n\ +KP_3: kbdKeyPress(1/02) \n\ +KP_3: kbdKeyRelease(1/02) \n\ +:plus: kbdKeyPress(0/02) \n\ +:plus: kbdKeyRelease(0/02) \n\ +KP_Add: kbdKeyPress(0/02) \n\ +KP_Add: kbdKeyRelease(0/02) \n\ +Escape: kbdKeyPress(*) \n\ +Escape: kbdKeyRelease(*) \n\ +0: kbdKeyPress(3/01) \n\ +0: kbdKeyRelease(3/01) \n\ +KP_0: kbdKeyPress(3/01) \n\ +KP_0: kbdKeyRelease(3/01) \n\ +:period: kbdKeyPress(2/01) \n\ +:period: kbdKeyRelease(2/01) \n\ +KP_Decimal: kbdKeyPress(2/01) \n\ +KP_Decimal: kbdKeyRelease(2/01) \n\ +space: kbdKeyPress(1/01) \n\ +space: kbdKeyRelease(1/01) \n\ +Return: kbdKeyPress(0/01) \n\ +Return: kbdKeyRelease(0/01) \n\ +KP_Enter: kbdKeyPress(0/01) \n\ +KP_Enter: kbdKeyRelease(0/01) \n\ +Alt_L: kbdKeyPress(7/08) \n\ +Alt_L: kbdKeyRelease(7/08) \n\ +Alt_R: kbdKeyPress(7/08) \n\ +Alt_R: kbdKeyRelease(7/08) \n\ +Shift_L: kbdKeyPress(7/04) \n\ +Shift_L: kbdKeyRelease(7/04) \n\ +Shift_R: kbdKeyPress(7/02) \n\ +Shift_R: kbdKeyRelease(7/02) + + +! ----------------------------------------------------------------------------- +! The following settings act on the hp40 face only + +! Background color + +*hp40*background: midnight blue + +! XmRowColumn container settings; vertical layout, packed + +*hp40.numColumns: 1 +*hp40.orientation: XmVERTICAL +*hp40.packing: XmPACK_TIGHT + +! Keyboard layout + +*hp40.nKeys: 51 +*hp40.kbd.fractionBase: 1440 +*hp40.kbd*fontList: *helvetica-*-r-*-*-12-*,*symbol-*-*-*-*-12-*=S +*hp40.kbd*btn.fontList: *helvetica-*-r-*-*-14-*,*symbol-*-*-*-*-14-*=S +*hp40.kbd*ul.foreground: light sky blue +*hp40.kbd*ur.foreground: red +*hp40.kbd*lr.foreground: orange +*hp40.kbd*btn.background: gray20 +*hp40.kbd*btn.foreground: gray90 +*hp40.kbd*btn.shadowThickness: 1 + +! Lcd screen colors and layout + +*hp40.frame*background: gray20 +*hp40.frame.lcd.background: gray80 +*hp40*lcd.foreground: black +*hp40*lcd.childType: XmFRAME_TITLE_CHILD +*hp40*lcd.childHorizontalAlignment: XmALIGNMENT_CENTER +*hp40*lcd.childVerticalAlignment: XmALIGNMENT_WIDGET_BOTTOM +*hp40*lcd.resizePolicy: XmRESIZE_NONE +*hp40*lcd.width: 272 +*hp40*lcd.height: 156 + +! Suppress all lower-left and upper-right keyboard labels; +! the hp40 does not use them + +*hp40.kbd*ur.labelString: +*hp40.kbd*ll.labelString: + +! Resources for all keyboard keys +! First row + +*hp40.kbd.0.topPosition: 0 +*hp40.kbd.0.bottomPosition: 130 +*hp40.kbd.0.leftPosition: 0 +*hp40.kbd.0.rightPosition: 240 +*hp40.kbd.0.btn.background: gray75 +*hp40.kbd.0.btn.labelString: \ +*hp40.kbd.0.ul.labelString: \ +*hp40.kbd.0.lr.labelString: \ + +*hp40.kbd.1.topPosition: 0 +*hp40.kbd.1.bottomPosition: 130 +*hp40.kbd.1.leftPosition: 240 +*hp40.kbd.1.rightPosition: 480 +*hp40.kbd.1.btn.background: gray75 +*hp40.kbd.1.btn.labelString: \ +*hp40.kbd.1.ul.labelString: \ +*hp40.kbd.1.lr.labelString: \ + +*hp40.kbd.2.topPosition: 0 +*hp40.kbd.2.bottomPosition: 130 +*hp40.kbd.2.leftPosition: 480 +*hp40.kbd.2.rightPosition: 720 +*hp40.kbd.2.btn.background: gray75 +*hp40.kbd.2.btn.labelString: \ +*hp40.kbd.2.ul.labelString: \ +*hp40.kbd.2.lr.labelString: \ + +*hp40.kbd.3.topPosition: 0 +*hp40.kbd.3.bottomPosition: 130 +*hp40.kbd.3.leftPosition: 720 +*hp40.kbd.3.rightPosition: 960 +*hp40.kbd.3.btn.background: gray75 +*hp40.kbd.3.btn.labelString: \ +*hp40.kbd.3.ul.labelString: \ +*hp40.kbd.3.lr.labelString: \ + +*hp40.kbd.4.topPosition: 0 +*hp40.kbd.4.bottomPosition: 130 +*hp40.kbd.4.leftPosition: 960 +*hp40.kbd.4.rightPosition: 1200 +*hp40.kbd.4.btn.background: gray75 +*hp40.kbd.4.btn.labelString: \ +*hp40.kbd.4.ul.labelString: \ +*hp40.kbd.4.lr.labelString: \ + +*hp40.kbd.5.topPosition: 0 +*hp40.kbd.5.bottomPosition: 130 +*hp40.kbd.5.leftPosition: 1200 +*hp40.kbd.5.rightPosition: 1440 +*hp40.kbd.5.btn.background: gray75 +*hp40.kbd.5.btn.labelString: \ +*hp40.kbd.5.ul.labelString: \ +*hp40.kbd.5.lr.labelString: \ + +! Second row/third row + +*hp40.kbd.6.topPosition: 200 +*hp40.kbd.6.bottomPosition: 330 +*hp40.kbd.6.leftPosition: 0 +*hp40.kbd.6.rightPosition: 288 +*hp40.kbd.6.btn.background: gray75 +*hp40.kbd.6.btn.labelString: SYMB +*hp40.kbd.6.ul.labelString: \ +*hp40.kbd.6.lr.labelString: \ + +*hp40.kbd.7.topPosition: 200 +*hp40.kbd.7.bottomPosition: 330 +*hp40.kbd.7.leftPosition: 288 +*hp40.kbd.7.rightPosition: 576 +*hp40.kbd.7.btn.background: gray75 +*hp40.kbd.7.btn.labelString: PLOT +*hp40.kbd.7.ul.labelString: \ +*hp40.kbd.7.lr.labelString: \ + +*hp40.kbd.8.topPosition: 200 +*hp40.kbd.8.bottomPosition: 330 +*hp40.kbd.8.leftPosition: 576 +*hp40.kbd.8.rightPosition: 864 +*hp40.kbd.8.btn.background: gray75 +*hp40.kbd.8.btn.labelString: NUM +*hp40.kbd.8.ul.labelString: \ +*hp40.kbd.8.lr.labelString: \ + +*hp40.kbd.9.topPosition: 330 +*hp40.kbd.9.bottomPosition: 460 +*hp40.kbd.9.leftPosition: 0 +*hp40.kbd.9.rightPosition: 288 +*hp40.kbd.9.btn.background: gray75 +*hp40.kbd.9.btn.labelString: HOME +*hp40.kbd.9.ul.labelString: MODES +*hp40.kbd.9.lr.labelString: \ + +*hp40.kbd.10.topPosition: 330 +*hp40.kbd.10.bottomPosition: 460 +*hp40.kbd.10.leftPosition: 288 +*hp40.kbd.10.rightPosition: 576 +*hp40.kbd.10.btn.background: gray75 +*hp40.kbd.10.btn.labelString: APLET +*hp40.kbd.10.ul.labelString: NOTE +*hp40.kbd.10.lr.labelString: \ + +*hp40.kbd.11.topPosition: 330 +*hp40.kbd.11.bottomPosition: 460 +*hp40.kbd.11.leftPosition: 576 +*hp40.kbd.11.rightPosition: 864 +*hp40.kbd.11.btn.background: gray75 +*hp40.kbd.11.btn.labelString: VIEWS +*hp40.kbd.11.ul.labelString: SKETCH +*hp40.kbd.11.lr.labelString: \ + +! Cursor keys + +*hp40.kbd.12.topPosition: 130 +*hp40.kbd.12.bottomPosition: 260 +*hp40.kbd.12.leftPosition: 1008 +*hp40.kbd.12.rightPosition: 1296 +*hp40.kbd.12.btn.foreground: light sky blue +*hp40.kbd.12.btn.compoundString: #S\255 +*hp40.kbd.12.ul.labelString: \ +*hp40.kbd.12.lr.labelString: \ + +*hp40.kbd.13.topPosition: 260 +*hp40.kbd.13.bottomPosition: 390 +*hp40.kbd.13.leftPosition: 864 +*hp40.kbd.13.rightPosition: 1152 +*hp40.kbd.13.btn.foreground: light sky blue +*hp40.kbd.13.btn.compoundString: #S\254 +*hp40.kbd.13.ul.labelString: \ +*hp40.kbd.13.lr.labelString: \ + +*hp40.kbd.14.topPosition: 260 +*hp40.kbd.14.bottomPosition: 390 +*hp40.kbd.14.leftPosition: 1152 +*hp40.kbd.14.rightPosition: 1440 +*hp40.kbd.14.btn.foreground: light sky blue +*hp40.kbd.14.btn.compoundString: #S\256 +*hp40.kbd.14.ul.labelString: \ +*hp40.kbd.14.lr.labelString: \ + +*hp40.kbd.15.topPosition: 390 +*hp40.kbd.15.bottomPosition: 520 +*hp40.kbd.15.leftPosition: 1008 +*hp40.kbd.15.rightPosition: 1296 +*hp40.kbd.15.btn.foreground: light sky blue +*hp40.kbd.15.btn.compoundString: #S\257 +*hp40.kbd.15.ul.labelString: \ +*hp40.kbd.15.lr.labelString: \ + +! Fourth row + +*hp40.kbd.16.topPosition: 520 +*hp40.kbd.16.bottomPosition: 650 +*hp40.kbd.16.leftPosition: 0 +*hp40.kbd.16.rightPosition: 288 +*hp40.kbd.16.btn.background: gray50 +*hp40.kbd.16.btn.labelString: VARS +*hp40.kbd.16.ul.labelString: CHARS +*hp40.kbd.16.lr.labelString: A + +*hp40.kbd.17.topPosition: 520 +*hp40.kbd.17.bottomPosition: 650 +*hp40.kbd.17.leftPosition: 288 +*hp40.kbd.17.rightPosition: 576 +*hp40.kbd.17.btn.background: gray50 +*hp40.kbd.17.btn.labelString: MATH +*hp40.kbd.17.ul.labelString: CMDS +*hp40.kbd.17.lr.labelString: B + +*hp40.kbd.18.topPosition: 520 +*hp40.kbd.18.bottomPosition: 650 +*hp40.kbd.18.leftPosition: 576 +*hp40.kbd.18.rightPosition: 864 +*hp40.kbd.18.btn.background: gray50 +*hp40.kbd.18.btn.labelString: d/dx +*hp40.kbd.18.ul.compoundString: #S\362 +*hp40.kbd.18.lr.labelString: C + +*hp40.kbd.19.topPosition: 520 +*hp40.kbd.19.bottomPosition: 650 +*hp40.kbd.19.leftPosition: 864 +*hp40.kbd.19.rightPosition: 1152 +*hp40.kbd.19.btn.background: gray50 +*hp40.kbd.19.btn.compoundString: X,T,#S\161 +*hp40.kbd.19.ul.labelString: EEX +*hp40.kbd.19.lr.labelString: D + +*hp40.kbd.20.topPosition: 520 +*hp40.kbd.20.bottomPosition: 650 +*hp40.kbd.20.leftPosition: 1152 +*hp40.kbd.20.rightPosition: 1440 +*hp40.kbd.20.btn.background: gray50 +*hp40.kbd.20.btn.labelString: DEL +*hp40.kbd.20.ul.labelString: CLEAR +*hp40.kbd.20.lr.labelString: \ + +! Fifth row + +*hp40.kbd.21.topPosition: 650 +*hp40.kbd.21.bottomPosition: 780 +*hp40.kbd.21.leftPosition: 0 +*hp40.kbd.21.rightPosition: 288 +*hp40.kbd.21.btn.background: gray50 +*hp40.kbd.21.btn.labelString: SIN +*hp40.kbd.21.ul.labelString: ASIN +*hp40.kbd.21.lr.labelString: E + +*hp40.kbd.22.topPosition: 650 +*hp40.kbd.22.bottomPosition: 780 +*hp40.kbd.22.leftPosition: 288 +*hp40.kbd.22.rightPosition: 576 +*hp40.kbd.22.btn.background: gray50 +*hp40.kbd.22.btn.labelString: COS +*hp40.kbd.22.ul.labelString: ACOS +*hp40.kbd.22.lr.labelString: F + +*hp40.kbd.23.topPosition: 650 +*hp40.kbd.23.bottomPosition: 780 +*hp40.kbd.23.leftPosition: 576 +*hp40.kbd.23.rightPosition: 864 +*hp40.kbd.23.btn.background: gray50 +*hp40.kbd.23.btn.labelString: TAN +*hp40.kbd.23.ul.labelString: ATAN +*hp40.kbd.23.lr.labelString: G + +*hp40.kbd.24.topPosition: 650 +*hp40.kbd.24.bottomPosition: 780 +*hp40.kbd.24.leftPosition: 864 +*hp40.kbd.24.rightPosition: 1152 +*hp40.kbd.24.btn.background: gray50 +*hp40.kbd.24.btn.labelString: ln +*hp40.kbd.24.ul.labelString: e^x +*hp40.kbd.24.lr.labelString: H + +*hp40.kbd.25.topPosition: 650 +*hp40.kbd.25.bottomPosition: 780 +*hp40.kbd.25.leftPosition: 1152 +*hp40.kbd.25.rightPosition: 1440 +*hp40.kbd.25.btn.background: gray50 +*hp40.kbd.25.btn.labelString: log +*hp40.kbd.25.ul.labelString: 10^x +*hp40.kbd.25.lr.labelString: I + +! Sixth row + +*hp40.kbd.26.topPosition: 780 +*hp40.kbd.26.bottomPosition: 910 +*hp40.kbd.26.leftPosition: 0 +*hp40.kbd.26.rightPosition: 288 +*hp40.kbd.26.btn.background: gray50 +*hp40.kbd.26.btn.labelString: X^2 +*hp40.kbd.26.ul.compoundString: #S\326 +*hp40.kbd.26.lr.labelString: J + +*hp40.kbd.27.topPosition: 780 +*hp40.kbd.27.bottomPosition: 910 +*hp40.kbd.27.leftPosition: 288 +*hp40.kbd.27.rightPosition: 576 +*hp40.kbd.27.btn.background: gray50 +*hp40.kbd.27.btn.labelString: X^Y +*hp40.kbd.27.ul.compoundString: n#S\326 +*hp40.kbd.27.lr.labelString: K + +*hp40.kbd.28.topPosition: 780 +*hp40.kbd.28.bottomPosition: 910 +*hp40.kbd.28.leftPosition: 576 +*hp40.kbd.28.rightPosition: 864 +*hp40.kbd.28.btn.background: gray50 +*hp40.kbd.28.btn.labelString: ( +*hp40.kbd.28.ul.compoundString: ABS +*hp40.kbd.28.lr.labelString: L + +*hp40.kbd.29.topPosition: 780 +*hp40.kbd.29.bottomPosition: 910 +*hp40.kbd.29.leftPosition: 864 +*hp40.kbd.29.rightPosition: 1152 +*hp40.kbd.29.btn.background: gray50 +*hp40.kbd.29.btn.labelString: ) +*hp40.kbd.29.ul.compoundString: ARG +*hp40.kbd.29.lr.labelString: M + +*hp40.kbd.30.topPosition: 780 +*hp40.kbd.30.bottomPosition: 910 +*hp40.kbd.30.leftPosition: 1152 +*hp40.kbd.30.rightPosition: 1440 +*hp40.kbd.30.btn.foreground: orange +*hp40.kbd.30.btn.compoundString: #S\270 +*hp40.kbd.30.ul.labelString: X^-1 +*hp40.kbd.30.lr.labelString: N + +! Seventh row + +*hp40.kbd.31.topPosition: 910 +*hp40.kbd.31.bottomPosition: 1040 +*hp40.kbd.31.leftPosition: 0 +*hp40.kbd.31.rightPosition: 288 +*hp40.kbd.31.btn.background: gray50 +*hp40.kbd.31.btn.labelString: , +*hp40.kbd.31.ul.labelString: MEMORY +*hp40.kbd.31.lr.labelString: O + +*hp40.kbd.32.topPosition: 910 +*hp40.kbd.32.bottomPosition: 1040 +*hp40.kbd.32.leftPosition: 288 +*hp40.kbd.32.rightPosition: 576 +*hp40.kbd.32.btn.foreground: orange +*hp40.kbd.32.btn.labelString: 7 +*hp40.kbd.32.ul.labelString: LIST +*hp40.kbd.32.lr.labelString: P + +*hp40.kbd.33.topPosition: 910 +*hp40.kbd.33.bottomPosition: 1040 +*hp40.kbd.33.leftPosition: 576 +*hp40.kbd.33.rightPosition: 864 +*hp40.kbd.33.btn.foreground: orange +*hp40.kbd.33.btn.labelString: 8 +*hp40.kbd.33.ul.labelString: { +*hp40.kbd.33.lr.labelString: Q + +*hp40.kbd.34.topPosition: 910 +*hp40.kbd.34.bottomPosition: 1040 +*hp40.kbd.34.leftPosition: 864 +*hp40.kbd.34.rightPosition: 1152 +*hp40.kbd.34.btn.foreground: orange +*hp40.kbd.34.btn.labelString: 9 +*hp40.kbd.34.ul.labelString: } +*hp40.kbd.34.lr.labelString: R + +*hp40.kbd.35.topPosition: 910 +*hp40.kbd.35.bottomPosition: 1040 +*hp40.kbd.35.leftPosition: 1152 +*hp40.kbd.35.rightPosition: 1440 +*hp40.kbd.35.btn.foreground: orange +*hp40.kbd.35.btn.compoundString: #S\264 +*hp40.kbd.35.ul.labelString: ! +*hp40.kbd.35.lr.labelString: S + +! Eigth row + +*hp40.kbd.36.topPosition: 1040 +*hp40.kbd.36.bottomPosition: 1170 +*hp40.kbd.36.leftPosition: 0 +*hp40.kbd.36.rightPosition: 288 +*hp40.kbd.36.btn.background: gray50 +*hp40.kbd.36.btn.foreground: orange +*hp40.kbd.36.btn.labelString: ALPHA +*hp40.kbd.36.ul.labelString: alpha +*hp40.kbd.36.lr.labelString: \ + +*hp40.kbd.37.topPosition: 1040 +*hp40.kbd.37.bottomPosition: 1170 +*hp40.kbd.37.leftPosition: 288 +*hp40.kbd.37.rightPosition: 576 +*hp40.kbd.37.btn.foreground: orange +*hp40.kbd.37.btn.labelString: 4 +*hp40.kbd.37.ul.labelString: MATRIX +*hp40.kbd.37.lr.labelString: T + +*hp40.kbd.38.topPosition: 1040 +*hp40.kbd.38.bottomPosition: 1170 +*hp40.kbd.38.leftPosition: 576 +*hp40.kbd.38.rightPosition: 864 +*hp40.kbd.38.btn.foreground: orange +*hp40.kbd.38.btn.labelString: 5 +*hp40.kbd.38.ul.labelString: [ +*hp40.kbd.38.lr.labelString: U + +*hp40.kbd.39.topPosition: 1040 +*hp40.kbd.39.bottomPosition: 1170 +*hp40.kbd.39.leftPosition: 864 +*hp40.kbd.39.rightPosition: 1152 +*hp40.kbd.39.btn.foreground: orange +*hp40.kbd.39.btn.labelString: 6 +*hp40.kbd.39.ul.labelString: ] +*hp40.kbd.39.lr.labelString: V + +*hp40.kbd.40.topPosition: 1040 +*hp40.kbd.40.bottomPosition: 1170 +*hp40.kbd.40.leftPosition: 1152 +*hp40.kbd.40.rightPosition: 1440 +*hp40.kbd.40.btn.foreground: orange +*hp40.kbd.40.btn.labelString: - +*hp40.kbd.40.ul.compoundString: #S\320 +*hp40.kbd.40.lr.labelString: W + +! Ninth row + +*hp40.kbd.41.topPosition: 1170 +*hp40.kbd.41.bottomPosition: 1300 +*hp40.kbd.41.leftPosition: 0 +*hp40.kbd.41.rightPosition: 288 +*hp40.kbd.41.btn.background: gray50 +*hp40.kbd.41.btn.foreground: light sky blue +*hp40.kbd.41.btn.labelString: SHIFT +*hp40.kbd.41.ul.labelString: \ +*hp40.kbd.41.lr.labelString: \ + +*hp40.kbd.42.topPosition: 1170 +*hp40.kbd.42.bottomPosition: 1300 +*hp40.kbd.42.leftPosition: 288 +*hp40.kbd.42.rightPosition: 576 +*hp40.kbd.42.btn.foreground: orange +*hp40.kbd.42.btn.labelString: 1 +*hp40.kbd.42.ul.labelString: PROGRM +*hp40.kbd.42.lr.labelString: X + +*hp40.kbd.43.topPosition: 1170 +*hp40.kbd.43.bottomPosition: 1300 +*hp40.kbd.43.leftPosition: 576 +*hp40.kbd.43.rightPosition: 864 +*hp40.kbd.43.btn.foreground: orange +*hp40.kbd.43.btn.labelString: 2 +*hp40.kbd.43.ul.labelString: SYNTAX +*hp40.kbd.43.lr.labelString: Y + +*hp40.kbd.44.topPosition: 1170 +*hp40.kbd.44.bottomPosition: 1300 +*hp40.kbd.44.leftPosition: 864 +*hp40.kbd.44.rightPosition: 1152 +*hp40.kbd.44.btn.foreground: orange +*hp40.kbd.44.btn.labelString: 3 +*hp40.kbd.44.ul.compoundString: #Sp +*hp40.kbd.44.lr.labelString: Z + +*hp40.kbd.45.topPosition: 1170 +*hp40.kbd.45.bottomPosition: 1300 +*hp40.kbd.45.leftPosition: 1152 +*hp40.kbd.45.rightPosition: 1440 +*hp40.kbd.45.btn.foreground: orange +*hp40.kbd.45.btn.labelString: + +*hp40.kbd.45.ul.compoundString: #SS +*hp40.kbd.45.lr.labelString: SPACE + +! Tenth row + +*hp40.kbd.46.topPosition: 1300 +*hp40.kbd.46.bottomPosition: 1430 +*hp40.kbd.46.leftPosition: 0 +*hp40.kbd.46.rightPosition: 288 +*hp40.kbd.46.btn.background: gray75 +*hp40.kbd.46.btn.labelString: ON +*hp40.kbd.46.ul.labelString: OFF +*hp40.kbd.46.lr.foreground: gray75 +*hp40.kbd.46.lr.labelString: CANCEL + +*hp40.kbd.47.topPosition: 1300 +*hp40.kbd.47.bottomPosition: 1430 +*hp40.kbd.47.leftPosition: 288 +*hp40.kbd.47.rightPosition: 576 +*hp40.kbd.47.btn.foreground: orange +*hp40.kbd.47.btn.labelString: 0 +*hp40.kbd.47.ul.labelString: NOTEPAD +*hp40.kbd.47.lr.labelString: \ +*hp40.kbd.47.lr.compoundString: #S\161 + +*hp40.kbd.48.topPosition: 1300 +*hp40.kbd.48.bottomPosition: 1430 +*hp40.kbd.48.leftPosition: 576 +*hp40.kbd.48.rightPosition: 864 +*hp40.kbd.48.btn.foreground: orange +*hp40.kbd.48.btn.labelString: . +*hp40.kbd.48.ul.labelString: = +*hp40.kbd.48.lr.labelString: : + +*hp40.kbd.49.topPosition: 1300 +*hp40.kbd.49.bottomPosition: 1430 +*hp40.kbd.49.leftPosition: 864 +*hp40.kbd.49.rightPosition: 1152 +*hp40.kbd.49.btn.foreground: orange +*hp40.kbd.49.btn.labelString: (-) +*hp40.kbd.49.ul.labelString: AND +*hp40.kbd.49.lr.labelString: ; + +*hp40.kbd.50.topPosition: 1300 +*hp40.kbd.50.bottomPosition: 1430 +*hp40.kbd.50.leftPosition: 1152 +*hp40.kbd.50.rightPosition: 1440 +*hp40.kbd.50.btn.foreground: orange +*hp40.kbd.50.btn.labelString: ENTER +*hp40.kbd.50.ul.labelString: ANS +*hp40.kbd.50.lr.labelString: \ + +! inOut codes for all keys + +*hp40.kbd.0.btn.inOut: 5/01 +*hp40.kbd.1.btn.inOut: 5/02 +*hp40.kbd.2.btn.inOut: 5/04 +*hp40.kbd.3.btn.inOut: 5/08 +*hp40.kbd.4.btn.inOut: 5/10 +*hp40.kbd.5.btn.inOut: 5/20 + +*hp40.kbd.6.btn.inOut: 5/80 +*hp40.kbd.7.btn.inOut: 4/80 +*hp40.kbd.8.btn.inOut: 3/80 + +*hp40.kbd.9.btn.inOut: 2/80 +*hp40.kbd.10.btn.inOut: 1/80 +*hp40.kbd.11.btn.inOut: 0/80 + +*hp40.kbd.12.btn.inOut: 6/08 +*hp40.kbd.13.btn.inOut: 6/04 +*hp40.kbd.14.btn.inOut: 6/01 +*hp40.kbd.15.btn.inOut: 6/02 + +*hp40.kbd.16.btn.inOut: 4/40 +*hp40.kbd.17.btn.inOut: 3/40 +*hp40.kbd.18.btn.inOut: 2/40 +*hp40.kbd.19.btn.inOut: 1/40 +*hp40.kbd.20.btn.inOut: 0/40 + +*hp40.kbd.21.btn.inOut: 4/20 +*hp40.kbd.22.btn.inOut: 3/20 +*hp40.kbd.23.btn.inOut: 2/20 +*hp40.kbd.24.btn.inOut: 1/20 +*hp40.kbd.25.btn.inOut: 0/20 + +*hp40.kbd.26.btn.inOut: 4/10 +*hp40.kbd.27.btn.inOut: 3/10 +*hp40.kbd.28.btn.inOut: 2/10 +*hp40.kbd.29.btn.inOut: 1/10 +*hp40.kbd.30.btn.inOut: 0/10 + +*hp40.kbd.31.btn.inOut: 7/08 +*hp40.kbd.32.btn.inOut: 3/08 +*hp40.kbd.33.btn.inOut: 2/08 +*hp40.kbd.34.btn.inOut: 1/08 +*hp40.kbd.35.btn.inOut: 0/08 + +*hp40.kbd.36.btn.inOut: 7/04 +*hp40.kbd.37.btn.inOut: 3/04 +*hp40.kbd.38.btn.inOut: 2/04 +*hp40.kbd.39.btn.inOut: 1/04 +*hp40.kbd.40.btn.inOut: 0/04 + +*hp40.kbd.41.btn.inOut: 7/02 +*hp40.kbd.42.btn.inOut: 3/02 +*hp40.kbd.43.btn.inOut: 2/02 +*hp40.kbd.44.btn.inOut: 1/02 +*hp40.kbd.45.btn.inOut: 0/02 + +*hp40.kbd.46.btn.inOut: * +*hp40.kbd.47.btn.inOut: 3/01 +*hp40.kbd.48.btn.inOut: 2/01 +*hp40.kbd.49.btn.inOut: 1/01 +*hp40.kbd.50.btn.inOut: 0/01 + +! Shortcut keyboard translations associated with the lcd display widget. +! They must be listed in more-to-less-specific order; so, for example, +! translations involving BackSpace should *before* those involving Delete + +*hp40*baseTranslations: #override \n\ +F1: kbdKeyPress(5/01) \n\ +F1: kbdKeyRelease(5/01) \n\ +F2: kbdKeyPress(5/02) \n\ +F2: kbdKeyRelease(5/02) \n\ +F3: kbdKeyPress(5/04) \n\ +F3: kbdKeyRelease(5/04) \n\ +F4: kbdKeyPress(5/08) \n\ +F4: kbdKeyRelease(5/08) \n\ +F5: kbdKeyPress(5/10) \n\ +F5: kbdKeyRelease(5/10) \n\ +F6: kbdKeyPress(5/20) \n\ +F6: kbdKeyRelease(5/20) \n\ +Home: kbdKeyPress(2/80) \n\ +Home: kbdKeyRelease(2/80) \n\ +Up: kbdKeyPress(6/08) \n\ +Up: kbdKeyRelease(6/08) \n\ +Left: kbdKeyPress(6/04) \n\ +Left: kbdKeyRelease(6/04) \n\ +Right: kbdKeyPress(6/01) \n\ +Right: kbdKeyRelease(6/01) \n\ +Down: kbdKeyPress(6/02) \n\ +Down: kbdKeyRelease(6/02) \n\ +A: kbdKeyPress(4/40) \n\ +A: kbdKeyRelease(4/40) \n\ +B: kbdKeyPress(3/40) \n\ +B: kbdKeyRelease(3/40) \n\ +C: kbdKeyPress(2/40) \n\ +C: kbdKeyRelease(2/40) \n\ +D: kbdKeyPress(1/40) \n\ +D: kbdKeyRelease(1/40) \n\ +BackSpace: kbdKeyPress(0/40) \n\ +BackSpace: kbdKeyRelease(0/40) \n\ +E: kbdKeyPress(4/20) \n\ +E: kbdKeyRelease(4/20) \n\ +F: kbdKeyPress(3/20) \n\ +F: kbdKeyRelease(3/20) \n\ +G: kbdKeyPress(2/20) \n\ +G: kbdKeyRelease(2/20) \n\ +H: kbdKeyPress(1/20) \n\ +H: kbdKeyRelease(1/20) \n\ +I: kbdKeyPress(0/20) \n\ +I: kbdKeyRelease(0/20) \n\ +J: kbdKeyPress(4/10) \n\ +J: kbdKeyRelease(4/10) \n\ +K: kbdKeyPress(3/10) \n\ +K: kbdKeyRelease(3/10) \n\ +L: kbdKeyPress(2/10) \n\ +L: kbdKeyRelease(2/10) \n\ +M: kbdKeyPress(1/10) \n\ +M: kbdKeyRelease(1/10) \n\ +M: kbdKeyPress(0/10) \n\ +M: kbdKeyRelease(0/10) \n\ +:slash: kbdKeyPress(0/10) \n\ +:slash: kbdKeyRelease(0/10) \n\ +KP_Divide: kbdKeyPress(0/10) \n\ +KP_Divide: kbdKeyRelease(0/10) \n\ +N: kbdKeyPress(0/10) \n\ +N: kbdKeyRelease(0/10) \n\ +comma: kbdKeyPress(7/08) \n\ +comma: kbdKeyRelease(7/08) \n\ +O: kbdKeyPress(7/08) \n\ +O: kbdKeyRelease(7/08) \n\ +7: kbdKeyPress(3/08) \n\ +7: kbdKeyRelease(3/08) \n\ +KP_7: kbdKeyPress(3/08) \n\ +KP_7: kbdKeyRelease(3/08) \n\ +P: kbdKeyPress(3/08) \n\ +P: kbdKeyRelease(3/08) \n\ +8: kbdKeyPress(2/08) \n\ +8: kbdKeyRelease(2/08) \n\ +KP_8: kbdKeyPress(2/08) \n\ +KP_8: kbdKeyRelease(2/08) \n\ +Q: kbdKeyPress(2/08) \n\ +Q: kbdKeyRelease(2/08) \n\ +9: kbdKeyPress(1/08) \n\ +9: kbdKeyRelease(1/08) \n\ +KP_9: kbdKeyPress(1/08) \n\ +KP_9: kbdKeyRelease(1/08) \n\ +R: kbdKeyPress(1/08) \n\ +R: kbdKeyRelease(1/08) \n\ +:asterisk: kbdKeyPress(0/08) \n\ +:asterisk: kbdKeyRelease(0/08) \n\ +KP_Multiply: kbdKeyPress(0/08) \n\ +KP_Multiply: kbdKeyRelease(0/08) \n\ +S: kbdKeyPress(0/08) \n\ +S: kbdKeyRelease(0/08) \n\ +Alt_L: kbdKeyPress(7/04) \n\ +Alt_L: kbdKeyRelease(7/04) \n\ +Alt_R: kbdKeyPress(7/04) \n\ +Alt_R: kbdKeyRelease(7/04) \n\ +4: kbdKeyPress(3/04) \n\ +4: kbdKeyRelease(3/04) \n\ +KP_4: kbdKeyPress(3/04) \n\ +KP_4: kbdKeyRelease(3/04) \n\ +T: kbdKeyPress(3/04) \n\ +T: kbdKeyRelease(3/04) \n\ +5: kbdKeyPress(2/04) \n\ +5: kbdKeyRelease(2/04) \n\ +KP_5: kbdKeyPress(2/04) \n\ +KP_5: kbdKeyRelease(2/04) \n\ +U: kbdKeyRelease(2/04) \n\ +U: kbdKeyPress(2/04) \n\ +6: kbdKeyPress(1/04) \n\ +6: kbdKeyRelease(1/04) \n\ +KP_6: kbdKeyPress(1/04) \n\ +KP_6: kbdKeyRelease(1/04) \n\ +V: kbdKeyPress(1/04) \n\ +V: kbdKeyRelease(1/04) \n\ +:minus: kbdKeyPress(0/04) \n\ +:minus: kbdKeyRelease(0/04) \n\ +KP_Subtract: kbdKeyPress(0/04) \n\ +KP_Subtract: kbdKeyRelease(0/04) \n\ +W: kbdKeyPress(0/04) \n\ +W: kbdKeyRelease(0/04) \n\ +Shift_L: kbdKeyPress(7/02) \n\ +Shift_L: kbdKeyRelease(7/02) \n\ +Shift_R: kbdKeyPress(7/02) \n\ +Shift_R: kbdKeyRelease(7/02) \n\ +1: kbdKeyPress(3/02) \n\ +1: kbdKeyRelease(3/02) \n\ +KP_1: kbdKeyPress(3/02) \n\ +KP_1: kbdKeyRelease(3/02) \n\ +X: kbdKeyPress(3/02) \n\ +X: kbdKeyRelease(3/02) \n\ +2: kbdKeyPress(2/02) \n\ +2: kbdKeyRelease(2/02) \n\ +KP_2: kbdKeyPress(2/02) \n\ +KP_2: kbdKeyRelease(2/02) \n\ +Y: kbdKeyPress(2/02) \n\ +Y: kbdKeyRelease(2/02) \n\ +3: kbdKeyPress(1/02) \n\ +3: kbdKeyRelease(1/02) \n\ +KP_3: kbdKeyPress(1/02) \n\ +KP_3: kbdKeyRelease(1/02) \n\ +Z: kbdKeyPress(1/02) \n\ +Z: kbdKeyRelease(1/02) \n\ +:plus: kbdKeyPress(0/02) \n\ +:plus: kbdKeyRelease(0/02) \n\ +KP_Add: kbdKeyPress(0/02) \n\ +KP_Add: kbdKeyRelease(0/02) \n\ +space: kbdKeyPress(0/02) \n\ +space: kbdKeyRelease(0/02) \n\ +Escape: kbdKeyPress(*) \n\ +Escape: kbdKeyRelease(*) \n\ +0: kbdKeyPress(3/01) \n\ +0: kbdKeyRelease(3/01) \n\ +KP_0: kbdKeyPress(3/01) \n\ +KP_0: kbdKeyRelease(3/01) \n\ +:period: kbdKeyPress(2/01) \n\ +:period: kbdKeyRelease(2/01) \n\ +KP_Decimal: kbdKeyPress(2/01) \n\ +KP_Decimal: kbdKeyRelease(2/01) \n\ +:semicolon: kbdKeyPress(1/01) \n\ +:semicolon: kbdKeyRelease(1/01) \n\ +Return: kbdKeyPress(0/01) \n\ +Return: kbdKeyRelease(0/01) \n\ +Tab: kbdKeyPress(0/01) \n\ +Tab: kbdKeyRelease(0/01) \n\ +KP_Enter: kbdKeyPress(0/01) \n\ +KP_Enter: kbdKeyRelease(0/01) diff --git a/args.h b/args.h new file mode 100644 index 0000000..4eac969 --- /dev/null +++ b/args.h @@ -0,0 +1,100 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: args.h,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: args.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 19-Jan-1998 +.keywords : * +.description : + This header declares a global data structure containing the emulator + invocation arguments; this data structure is initialized before startup, + either by means of argc/argv or in other ways. + +.include : config.h + +.notes : + $Log: args.h,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.15 2000/11/15 14:03:06 cibrario + GUI enhancements and assorted bug fixes: + - added .batchXfer boolean (corresponding to command-line option + -batchXfer) to struct Args + + Revision 3.10 2000/10/24 16:14:25 cibrario + Added/Replaced GPL header + + Revision 2.1 2000/09/08 14:45:06 cibrario + Added the following fields to 'struct Args': reset, monitor, hw. + The latter mimes the setting of the -hw command line option and + of the *hw top-level application resource; the former two have + been added to export them cleanly from the GUI modules. + + * Revision 1.1 1998/02/17 11:58:37 cibrario + * Initial revision + * + +.- */ + +/*--------------------------------------------------------------------------- + Data type definitions - require config.h + ---------------------------------------------------------------------------*/ + +struct Args +{ + int reset; /* 2.1: Force emulator reset */ + int monitor; /* 2.1: Call monitor() on startup */ + int batchXfer; /* 3.15: Non-interactive file transfers */ + char *mod_file_name; + char *cpu_file_name; + char *hdw_file_name; + char *rom_file_name; + char *ram_file_name; + char *port_1_file_name; + char *port_2_file_name; + char *hw; /* 2.1: Hardware configuration (unused) */ +}; + + +/*--------------------------------------------------------------------------- + Global variables + ---------------------------------------------------------------------------*/ + +extern struct Args args; diff --git a/cindex.texi b/cindex.texi new file mode 100644 index 0000000..45bb841 --- /dev/null +++ b/cindex.texi @@ -0,0 +1,5 @@ +@c $Id: cindex.texi,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + +@node Concept Index, , GNU GENERAL PUBLIC LICENSE, Top +@unnumbered Concept Index +@printindex cp diff --git a/clopt.texi b/clopt.texi new file mode 100644 index 0000000..7bf330d --- /dev/null +++ b/clopt.texi @@ -0,0 +1,148 @@ +@c $Id: clopt.texi,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + +@node Command Line Options, Customizing saturn, Using the Emulator, Top +@chapter Command Line Options +@cindex Command Line Options + +The @code{saturn} program accepts the command line options listed below; +notice that recent versions of the X Toolkit allow you to abbreviate +option's names to their shortest, non-ambiguous prefix. + +@table @code +@item -reset +This option instructs @code{saturn} to reset the emulated CPU, +by forcing a jump to address @code{0}, before starting execution. +It should be useful when the emulated calculator ``gets stuck'', since +it has the same effect as pressing the reset switch on the real +calculator. + +@item -monitor +This option instructs @code{saturn} to enter the monitor/debugger +during startup; since this feature of @code{saturn} is pitiful, +use of this option is strongly discouraged for now. + +@item -batchXfer +When this option is present on the command line, all fast file +transfers requested through the @code{sutil} library will be +made in batch mode, that is, @code{saturn} will always load and save +files in the current directory and will use the default file names +given by the calculator, without any user interaction. + +@item -stateDir @var{dir_name} +This option must be followed by the name of a directory, @var{dir_name}; +@code{saturn} will use this directory to load/save the calculator's +state. By specifying different state directories, you can emulate +multiple calculator models and/or multiple instances of the same +calculator, even simultaneously. However, keep in mind that +calculator's state files are @strong{not} platform-independent, so you +cannot share them among different platforms. The default value of this +option is @code{.}, that is, by default @code{saturn} places its +state files in the current directory. + +@item -cpu @var{cpu_state} +This option must be followed by the name of a file, @var{cpu_state}, +relative to the emulator's state directory; @code{saturn} will use +this file to load/save the calculator's emulated CPU state. +The default value of this option is @code{cpu}. + +@item -mod @var{mod_state} +This option must be followed by the name of a file, @var{mod_state}, +relative to the emulator's state directory; @code{saturn} will use +this file to load/save the calculator's emulated peripheral modules +state. +The default value of this option is @code{mod}. + +@item -hdw @var{hdw_state} +This option must be followed by the name of a file, @var{hdw_state}, +relative to the emulator's state directory; @code{saturn} will use +this file to load/save the calculator's emulated peripheral devices +state. +The default value of this option is @code{hdw}. + +@item -rom @var{rom_image} +This option must be followed by the name of a file, @var{rom_image}, +relative to the emulator's state directory; @code{saturn} will use +this file to load (and save, when emulating an HP49) the calculator's +ROM image. +The default value of this option is @code{rom}. + +@item -ram @var{ram_image} +This option must be followed by the name of a file, @var{ram_image}, +relative to the emulator's state directory; @code{saturn} will use +this file to load/save the calculator's main RAM image. +The default value of this option is @code{ram}. + +@item -port1 @var{port1_image} +This option must be followed by the name of a file, @var{port1_image}, +relative to the emulator's state directory; @code{saturn} will use +this file to load/save the calculator's port 1 image. +The default value of this option is @code{port1}. +This option is meaningful only when emulating an HP48GX. + +@item -port2 @var{port2_image} +This option must be followed by the name of a file, @var{port2_image}, +relative to the emulator's state directory; @code{saturn} will use +this file to load/save the calculator's port 2 image. +The default value of this option is @code{port2}. +This option is meaningful only when emulating an HP48GX. + +@item -face @var{face_name} +This option must be followed by the name of a faceplate, @var{face_name}, +defined in the application resources of @code{saturn}. The faceplate +defines the ``look'' of the emulated calculator, such as its color +and the size and position of its keys, the mapping between +calculator's keys and IN/OUT codes seen by the CPU, and keyboard +shortcuts. Each user can define its own faceplates for @code{saturn}, +and use them without rebuilding the executable; for more information +see @ref{Customizing saturn}. The application resource file +distributed with @code{saturn} defines the following faceplates: + +@table @code +@item hp48 +Naive HP48GX faceplate +@item hp49 +Naive HP49 faceplate +@item hp40 +Naive HP40 faceplate +@end table + +The default value of this option is @code{hp48}, corresponding +to a naive HP48GX faceplate in the default application resource +file distributed with @code{saturn}. + +@item -hw @var{hw_name} +This option must be followed by the name of an hardware configuration, +@var{hw_name}, chosen among those supported by @code{saturn}. +The hardware configuration defines things like ROM/RAM sizes, bank +switching mechanism, and so on. Currently, @code{saturn} supports two +hardware configurations: + +@table @code +@item hp48 +HP48GX calculator +@item hp49 +HP49 calculator, also suitable to emulate the HP40 +@end table + +Even if @code{saturn} has a modular structure, unfortunately +general users cannot add custom hardware architectures to it +without rebuilding the executable. Moreover, since I am a lazy guy, +the steps required to do this are not documented at all. +@end table + +In addition, @code{saturn} understands all standard X Toolkit command-line +options, and does its best to honor them. Among such options, the +most useful ones are: + +@table @code +@item -display @var{display_name} +This option must be followed by a valid X display name, and instructs +the emulator to use that display to show its windows. If this option +is not present on the command line the default display, that is, +the display named by the @code{DISPLAY} environment variable, is used. +@item -xrm @var{resource_spec} +This option must be followed by a valid X resource specifier, and +allows you to add/override one or more @code{saturn}'s X resources +``on-the-fly''. +@end table + diff --git a/config.h b/config.h new file mode 100644 index 0000000..d4338dd --- /dev/null +++ b/config.h @@ -0,0 +1,267 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: config.h,v 4.1.1.1 2002/11/11 16:13:29 cibrario Exp $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: config.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 28-Jan-1998 +.keywords : * +.description : + This file configures the Saturn / HP48 emulator. + +.include : * + +.notes : + $Log: config.h,v $ + Revision 4.1.1.1 2002/11/11 16:13:29 cibrario + Small screen support; preliminary + + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.16 2000/11/21 16:38:23 cibrario + Ultrix/IRIX support: + - Include config_x.h (automatic exceptions to user configuration) + + Revision 3.14 2000/11/13 10:23:43 cibrario + Implemented fast load/save; improved keyboard interface emulation at + high emulated CPU speed: + + - New configuration option: CPU_SLOW_IN (enabled by default) + + Revision 3.13 2000/11/09 11:21:18 cibrario + Revised to add file selection box GUI element, CPU halt/run + requests and emulator's extended functions: + + - Updated documentation of CPU_SPIN_SHUTDN and REAL_CPU_SPEED + - Defined new Chf module identifier: X_FUNC_CHF_MODULE_ID + + Revision 3.10 2000/10/24 16:14:25 cibrario + Added/Replaced GPL header + + Revision 3.6 2000/10/02 13:52:32 cibrario + ROM handling utilities: + - Added new Chf module identifier: UTIL_CHF_MODULE_ID + + Revision 3.5 2000/10/02 09:40:35 cibrario + Linux support: + - documented new compile-time option: REAL_CPU_SPEED + + Revision 3.3 2000/09/26 15:24:20 cibrario + Revised to implement Flash ROM write access: + - Added new Chf module identifier FLASH_CHF_MODULE_ID + + * Revision 3.2 2000/09/22 13:34:43 cibrario + * Implemented preliminary support of HP49 hw architecture: + * - Re-enabled DEBUG + * - Documented new HP49_SUPPORT option, and enabled it by default + * + * Revision 3.1 2000/09/20 14:15:42 cibrario + * Revised to implement passive CPU shutdown: + * - disabled DEBUG option by default + * - enhanced documentation of CPU_SPIN_SHUTDN; this option is now + * disabled by default. + * + * Revision 2.6 2000/09/15 09:28:48 cibrario + * Enhanced documentation of serial emulation options; added + * template of SERIAL_FORCE_STREAMSPTY definition (commented out + * by default) + * + * Revision 2.5 2000/09/14 15:40:24 cibrario + * - Added description and default values of configuration options + * SERIAL_FORCE_OPENPTY and SERIAL_FORCE_STREAMSPTY. + * - Added new Chf module identifier SERIAL_CHF_MODULE_ID, used by the + * serial port emulation modules. + * + * Revision 2.4 2000/09/12 15:50:40 cibrario + * Added description and default definition of N_PORT_2_BANK + * + * Revision 2.1 2000/09/08 15:43:46 cibrario + * Disabled DEBUG option by default; documented new GUI option + * FORCE_NONMODAL. + * + * Revision 1.1 1998/02/17 14:57:15 cibrario + * Initial revision + * + +.- */ + + +/* CHF_EXTENDED_INFO: + Define this symbol if extended information is desired during condition + handling; this is usually useful only for debugging purposes. +*/ +/* #define CHF_EXTENDED_INFO */ + + +/* DEBUG: + Define this symbol to include the debugging code for all source modules + in the executable image. + + At run-time, the debug level can be set using the function SetDebugLevel(); + the initial debug level is set to the value of the symbol DEBUG_LEVEL, + if it is defined, otherwise it is set to zero. +*/ +/* #define DEBUG */ + + +/* DEBUG_LEVEL: + When this symbol is defined and the debugging code has been included + in the executable image (DEBUG symbol set), the initial debug level + is set to its value. + + The value must be the bitwise OR of zero or more of the symbols + DEBUG_C_<> defined in debug.h; each of them corresponds to a class + of debugging conditions that can be individually enabled or disabled. +*/ +#define DEBUG_LEVEL DEBUG_C_REVISION + + +/* CPU_SPIN_SHUTDN + If this symbol is defined, the cpu module implements the SHUTDN + instruction as a spin loop; when the instruction is encountered in the + run stream, the program counter is reset to the starting nibble of its + opcode. + + If this symbol is not defined, the cpu module implements the SHUTDN + instruction signaling a Chf condition; the main emulator loop + condition handler works in concert with the GUI module to implement + an idle loop when this condition is handled. + + This option MUST be defined if the revision of either the cpu emulation + module (cpu.c) or the GUI module (x11.c) is less than 3.1. + + Starting from release 3.1, this option is disabled by default, to + waste as little (real) cpu time as possible; however, expect a + loss of timing accuracy. + + Notice also that when this function is defined the CpuHaltRequest() + and CpuRunRequest() functions are disabled; as a consequence, all + interactive emulator's extended functions will be disabled as well. +*/ +/* #define CPU_SPIN_SHUTDN */ + + +/* 2.1: FORCE_NONMODAL + If this symbol is defined, nonmodal navigation is forced in the + OSF/Motif GUI, by setting navigationType to XmNONE and traversalOn + to False at the source code level. +*/ +/* #define FORCE_NONMODAL */ + + +/* 2.4: N_PORT_2_BANK + This symbol is used to dimension the HP48GX Port_2: it denotes the + number of 128 Kbyte banks the port must have and must be a power of 2 + between 1 and 32, inclusive. When undefined, Port_2 is not emulated at all. + The default value is 8, that is, Port_2 is emulated and its size is 1Mbyte. +*/ +#define N_PORT_2_BANK 8 + + +/* 2.5: SERIAL_FORCE_OPENPTY, SERIAL_FORCE_STREAMSPTY + Optionally define exactly one of these symbols to force the use of a + particular pty implementation; if no symbols are defined, the + serial port emulation modules will do their best to automatically + determine the most appropriate implementation for the platform at hand. +*/ +/* #define SERIAL_FORCE_OPENPTY */ +/* #define SERIAL_FORCE_STREAMSPTY */ + + +/* 3.2: HP49_SUPPORT + Define this symbol to enable HP49-specific support code in the + emulator; it does not harm if this symbol is defined when emulating + a HP48, too, since all changed should be backward-compatible. +*/ +#define HP49_SUPPORT + + +/* 3.13: REAL_CPU_SPEED + Define this symbol (recommended) to force the emulated CPU to run + no faster than a software-controlled limit; by default, the limit + is close to the real CPU speed. +*/ +#define REAL_CPU_SPEED + + +/* 3.14: CPU_SLOW_IN + Define this symbol (recommended) to slow down the A=IN and C=IN + instructions depending on the current emulated CPU speed. + + The value of the macro determines the gain of the relation between + CPU speed and slow down ratio. +*/ +#define CPU_SLOW_IN 16 + + +/* 4.1.1.1: LCD_MAG + This symbol represents the magnification ratio of the emulated LCD pixels + when drawn on the emulated display; supported values are 1 and 2; 2 is the + default. Currently, the value cannot be set at runtime for performance + reasons. +*/ +#define LCD_MAG 1 + + +/* 4.1.1.1: When defined, this symbol represents the threshold of the long + key pression. When the mouse button is kept pressed on a calculator's key + for more than LONG_PRESS_THR milliseconds, the key stays pressed after + release. +*/ +#define LONG_PRESS_THR 1000 + + +/* Chf Module Identifiers: + Each main module of the emulator has its own Chf Module Identifier; the + values defined here must match those actually used in the message catalogs. +*/ +#define MAIN_CHF_MODULE_ID 10 +#define CPU_CHF_MODULE_ID 11 +#define MOD_CHF_MODULE_ID 12 +#define DISK_IO_CHF_MODULE_ID 13 +#define X11_CHF_MODULE_ID 14 +#define SERIAL_CHF_MODULE_ID 15 /* 2.5 */ +#define FLASH_CHF_MODULE_ID 16 /* 3.3 */ +#define UTIL_CHF_MODULE_ID 17 /* 3.6 */ +#define X_FUNC_CHF_MODULE_ID 18 /* 3.13 */ +#define DEBUG_CHF_MODULE_ID 30 + + +/* 3.16: Include automatic exceptions to user configuration */ +#include "config_x.h" diff --git a/config_x.h b/config_x.h new file mode 100644 index 0000000..335e0cf --- /dev/null +++ b/config_x.h @@ -0,0 +1,65 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: config_x.h,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: config_x.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 20-Nov-2000 +.keywords : * +.description : + This file is included by config.h and automatically disables any + user configuration option known to be unsupported on the current platform. + +.include : * + +.notes : + $Log: config_x.h,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.16 2000/11/21 16:38:44 cibrario + *** empty log message *** + + +.- */ + + +/* REAL_CPU_SPEED is not supported on ultrix, because there is no usleep() */ +#ifdef ultrix +#undef REAL_CPU_SPEED +#endif diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..5b6bc85 --- /dev/null +++ b/cpu.c @@ -0,0 +1,3185 @@ + +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: cpu.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: cpu.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 2-Feb-1998 +.keywords : * +.description : + This file executes the Saturn CPU opcodes. References: + + SASM.DOC by HP (HORN disk 4) + Guide to the Saturn Processor Rev. 0.00f by Matthew Mastracci + entries.srt by Mika Heiskanen (mheiskan@vipunen.hut.fi) + x48 source code by Eddie C. Dost (ecd@dressler.de) + +.include : config.h, machdep.h, cpu.h + +.notes : + $Log: cpu.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.14 2000/11/13 10:30:04 cibrario + Implemented fast load/save; improved keyboard interface emulation at + high emulated CPU speed: + + - Added a delay loop in ExecIN(), when CPU_SLOW_IN is defined. the loop + is implemented executing the same instruction multiple times and is + needed because the HP firmware uses an active loop instead of a + timer to determine the keyboard automatic repeat rate. + + - Changed initial value of cpu_status.inner_loop_max after a CPU reset, + to be as documented (that is, maximum speed). + + - During CPU initialization, both shutdn and halt flags are now + resetted. + + Revision 3.13 2000/11/09 11:23:12 cibrario + Revised to add file selection box GUI element, CPU halt/run + requests and emulator's extended functions: + + - Implemented CpuHaltRequest(), CpuRunRequest(), CpuHaltAllowed() + + Revision 3.10 2000/10/24 16:14:28 cibrario + Added/Replaced GPL header + + Revision 3.5 2000/10/02 09:42:09 cibrario + Linux support: + - gcc does not like array subscripts with type 'char', and it is right. + + Revision 3.1 2000/09/20 13:39:18 cibrario + Minor updates and fixes to avoid gcc compiler warnings on Solaris + when -ansi -pedantic -Wall options are selected. + + * Revision 1.2 2000/09/07 14:31:34 cibrario + * Bug fix: cpu_status.return_sp and .reset_req were not reset; this gave + * troubles when attempting to override a corrupt status with CpuReset(). + * + * Revision 1.1 1998/02/17 15:25:16 cibrario + * Initial revision + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: cpu.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "keyb.h" +#include "disk_io.h" /* 3.1: ReadStructFromFile/WriteStructToFile */ +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID CPU_CHF_MODULE_ID +#include + +#define GetNibble FetchNibble + + +/*--------------------------------------------------------------------------- + Global variables + ---------------------------------------------------------------------------*/ + +struct CpuStatus cpu_status; + + +/*--------------------------------------------------------------------------- + Private variables + ---------------------------------------------------------------------------*/ + + +/* Field selector indexes, lo/hi nibble. + NOTE: The P and WP elements of the array must be dynamically adjusted + since they depend on the current value of the P CPU register +*/ +static const int fs_idx_lo[N_FS] = +/* P, WP, XS, X, S, M, B, W + ??, ??, ??, ??, ??, ??, ??, A +*/ +{ 0, 0, 2, 0, 15, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const int fs_idx_hi[N_FS] = +/* P, WP, XS, X, S, M, B, W + ??, ??, ??, ??, ??, ??, ??, A +*/ +{ 0, 0, 2, 2, 15, 14, 1, 15, + 0, 0, 0, 0, 0, 0, 0, 4 +}; + + +/* Register Pair pointers */ +static Nibble *const reg_pair_0[] = +/* AB, BC, CA, DC */ +{ cpu_status.A, cpu_status.B, cpu_status.C, cpu_status.D }; + +static Nibble *const reg_pair_1[] = +/* AB, BC, CA, DC */ +{ cpu_status.B, cpu_status.C, cpu_status.A, cpu_status.C }; + + +/* Nibble bit masks */ +static const Nibble nibble_bit_mask[] = +{ 0x1, 0x2, 0x4, 0x8 }; + + +/* ProgramStatusRegister bit masks */ +static const ProgramStatusRegister st_bit_mask[] = +{ 0x0001, 0x0002, 0x0004, 0x0008, + 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, + 0x1000, 0x2000, 0x4000, 0x8000 +}; + +/* Decimal sum/carry tables, range 0..31 */ +static const int dec_sum[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 1 +}; + +static const int dec_carry[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1 +}; + + +/* Decimal sub/borrow tables, range -10..15 */ +static const int dec_sub_t[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 1, 2, 3, 4, 5 +}; + +static const int dec_borrow_t[] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; + +static const int *const dec_sub = dec_sub_t + 10; +static const int *const dec_borrow = dec_borrow_t + 10; + + +/* Decimal one's complement table */ +static const int dec_one_c[] = +{ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0 +}; + + +/*--------------------------------------------------------------------------- + Private functions: return stack handling + ---------------------------------------------------------------------------*/ + +/* PushRSTK */ +static void PushRSTK(const Address r) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "PushRSTK"); + cpu_status.return_stack[cpu_status.return_sp] = r; + cpu_status.return_sp = (cpu_status.return_sp+1) & RETURN_SP_MASK; +} + +/* PopRSTK */ +static Address PopRSTK(void) +{ + Address r; + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "PopRSTK"); + cpu_status.return_sp = (cpu_status.return_sp-1) & RETURN_SP_MASK; + r = cpu_status.return_stack[cpu_status.return_sp]; + cpu_status.return_stack[cpu_status.return_sp] = (Address)0; + return r; +} + + +/*--------------------------------------------------------------------------- + Private functions: interrupt handling + ---------------------------------------------------------------------------*/ + +/* RTI */ +static void ExecRTI(void) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_INT, CPU_I_CALLED, "ExecRTI"); + + if(cpu_status.int_pending != INT_REQUEST_NONE) + { + debug1(DEBUG_C_INT, CPU_I_RTI_LOOP, + (cpu_status.int_pending == INT_REQUEST_NMI ? "NMI" : "IRQ")); + + /* Service immediately any pending interrupt request */ + cpu_status.int_service = 1; + cpu_status.int_pending = INT_REQUEST_NONE; + cpu_status.PC = INT_HANDLER_PC; + } + + else + { + /* Reenable interrupts and return */ + debug0(DEBUG_C_INT, CPU_I_RTI_END); + + cpu_status.int_service = 0; + cpu_status.PC = PopRSTK(); + } +} + +/* RSI */ +static void ExecRSI(void) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_INT, CPU_I_CALLED, "ExecRSI"); + + /* Discard last nibble of RSI opcode */ + cpu_status.PC++; + + KeybRSI(); +} + +/* INTON */ +static void ExecINTON(void) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_INT, CPU_I_CALLED, "ExecINTON"); + + /* Enable maskable interrupts */ + cpu_status.int_enable = 1; +} + +/* INTOFF */ +static void ExecINTOFF(void) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_INT, CPU_I_CALLED, "ExecINTOFF"); + + cpu_status.int_enable = 0; +} + + +/*--------------------------------------------------------------------------- + Private functions: bus input/output + ---------------------------------------------------------------------------*/ + +/* BUSCB */ +static void ExecBUSCB(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCB"); + ChfCondition CPU_F_INTERR, CHF_WARNING, "BUSCB" ChfEnd; + ChfSignal(); +} + +/* BUSCC */ +static void ExecBUSCC(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCC"); + ChfCondition CPU_F_INTERR, CHF_WARNING, "BUSCC" ChfEnd; + ChfSignal(); +} + +/* BUSCD */ +static void ExecBUSCD(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCD"); + ChfCondition CPU_F_INTERR, CHF_WARNING, "BUSCD" ChfEnd; + ChfSignal(); +} + +/* SREQ */ +static void ExecSREQ(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecSREQ"); + ChfCondition CPU_F_INTERR, CHF_WARNING, "SREQ" ChfEnd; + ChfSignal(); +} + +/* OUTC */ +static void ExecOUTC(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecOUTC"); + + cpu_status.OUT = + ((OutputRegister)cpu_status.C[0]) | + ((OutputRegister)cpu_status.C[1] << 4) | + ((OutputRegister)cpu_status.C[2] << 8); +} + +/* OUTCS */ +static void ExecOUTCS(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecOUTCS"); + + cpu_status.OUT = + ((OutputRegister)cpu_status.C[0]) | (cpu_status.OUT & 0xFF0); +} + +/* IN */ +static void ExecIN(Nibble *r) +{ + /* In */ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecIN"); + +#ifdef CPU_SLOW_IN + /* We must slow the A=IN and C=IN instruction down a bit, depending + on the emulated CPU speed. This is necessary because the HP firmware + uses an active loop instead of a timer to determine the keyboard + automatic repeat rate. + + Since implementing a precise, tiny (~ 1 microsecond), passive delay + in unix is almost impossible, we chose to execute the same + instruction (A=IN or C=IN) multiple times by artificially resetting + the PC as appropriate. + + The number of repetions depends linearly, with gain CPU_SLOW_IN, + from the current value of cpu_status.inner_loop: + cpu_status.inner_loop==INNER_LOOP_MAX corresponds to the nominal + CPU speed of 4MHz and to a repetition rate of 1 (instructions are + executed once as usual). + */ + { + static int count_down = 0; + + /* Decrement counter; set PC back and return immediately if counter + was not zero (counter not expired yet). + */ + if(count_down-- != 0) + { + cpu_status.PC -= 3; + return; + } + + /* Counter expired; reset counter and execute the instruction */ + count_down = ((cpu_status.inner_loop + (INNER_LOOP_MAX/2)) + / INNER_LOOP_MAX) * CPU_SLOW_IN; + } +#endif + cpu_status.IN = KeybIN(cpu_status.OUT); + + r[0] = (Nibble)(cpu_status.IN & NIBBLE_MASK); + r[1] = (Nibble)((cpu_status.IN) >> 4 & NIBBLE_MASK); + r[2] = (Nibble)((cpu_status.IN) >> 8 & NIBBLE_MASK); + r[3] = (Nibble)((cpu_status.IN) >> 12 & NIBBLE_MASK); +} + + +/*--------------------------------------------------------------------------- + Private functions: CPU control + ---------------------------------------------------------------------------*/ + +static void ExecSHUTDN(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "SHUTDN"); + +#ifdef CPU_SPIN_SHUTDN + /* If the CPU_SPIN_SHUTDN symbol is defined, the CPU module implements + SHUTDN as a spin loop; the program counter is reset to the starting + nibble of the SHUTDN opcode. + */ + cpu_status.PC -= 3; +#endif + + /* Set shutdown flag */ + cpu_status.shutdn = 1; + +#ifndef CPU_SPIN_SHUTDN + /* If the CPU_SPIN_SHUTDN symbol is not defined, the CPU module implements + SHUTDN signalling the condition CPU_I_SHUTDN + */ + ChfCondition CPU_I_SHUTDN, CHF_INFO ChfEnd; + ChfSignal(); +#endif +} + + +/*--------------------------------------------------------------------------- + Private functions: data type conversions + ---------------------------------------------------------------------------*/ + +/* Copies the A field of a DataRegister into an Address; this is not a + loop to achieve greater execution speed. +*/ +static Address R2Addr(const Nibble *r) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "R2Addr"); + return( + ((Address)r[0] ) | + ((Address)r[1] << 4) | + ((Address)r[2] << 8) | + ((Address)r[3] << 12) | + ((Address)r[4] << 16) + ); +} + +/* Returns the nibs 0-3 of a DataRegister into an Address; this is not a + loop to achieve greater execution speed. +*/ +static Address R2AddrS(const Nibble *r) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "R2AddrS"); + return( + ((Address)r[0] ) | + ((Address)r[1] << 4) | + ((Address)r[2] << 8) | + ((Address)r[3] << 12) + ); +} + +/* Copies an Address into the A field of a register; this is not a loop + to achieve grater execution speed +*/ +static void Addr2R(Nibble *d, Address a) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "Addr2R"); + d[0] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[1] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[2] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[3] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[4] = (Nibble)(a & NIBBLE_MASK); +} + +/* Copies an Address into nibs 0-3 of a register; this is not a loop + to achieve grater execution speed +*/ +static void Addr2RS(Nibble *d, Address a) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "Addr2RS"); + d[0] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[1] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[2] = (Nibble)(a & NIBBLE_MASK); a >>= 4; + d[3] = (Nibble)(a & NIBBLE_MASK); +} + +/* Copy the 12 low-order bits of ST into C */ +static void St2C(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "St2C"); + cpu_status.C[0] = (Nibble)(cpu_status.ST & NIBBLE_MASK); + cpu_status.C[1] = (Nibble)((cpu_status.ST >> 4) & NIBBLE_MASK); + cpu_status.C[2] = (Nibble)((cpu_status.ST >> 8) & NIBBLE_MASK); +} + +/* Copy the 12 low-order bits of C into ST */ +static void C2St(void) +{ + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "C2St"); + cpu_status.ST = + (ProgramStatusRegister)cpu_status.C[0] | + ((ProgramStatusRegister)cpu_status.C[1] << 4) | + ((ProgramStatusRegister)cpu_status.C[2] << 8) | + (cpu_status.ST & CLRST_MASK); +} + +/* Exchange the 12 low-order bits of C with the 12 low-order bits of ST */ +static void CStExch(void) +{ + ProgramStatusRegister tst = cpu_status.ST; + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "CStExch"); + cpu_status.ST = + (ProgramStatusRegister)cpu_status.C[0] | + ((ProgramStatusRegister)cpu_status.C[1] << 4) | + ((ProgramStatusRegister)cpu_status.C[2] << 8) | + (cpu_status.ST & CLRST_MASK); + + cpu_status.C[0] = (Nibble)(tst & NIBBLE_MASK); + cpu_status.C[1] = (Nibble)((tst >> 4) & NIBBLE_MASK); + cpu_status.C[2] = (Nibble)((tst >> 8) & NIBBLE_MASK); +} + + +/*--------------------------------------------------------------------------- + Private functions: data memory read/write + ---------------------------------------------------------------------------*/ + +/* Read a field of a DataRegister from memory */ +void ReadDAT(Nibble *d, Address s, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + for(n=lo; n<=hi; n++) + d[n] = ReadNibble(s++); +} + +/* Read a field of a DataRegister from memory, with immediate fs */ +void ReadDATImm(Nibble *d, Address s, int imm_fs) +{ + register int n; + + for(n=0; n<=imm_fs; n++) + d[n] = ReadNibble(s++); +} + +/* Write a field of a DataRegister into memory */ +void WriteDAT(Address d, const Nibble *r, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + for(n=lo; n<=hi; n++) + WriteNibble(d++, r[n]); +} + +/* Write a field of a DataRegister into memory, with immediate fs */ +void WriteDATImm(Address d, const Nibble *r, int imm_fs) +{ + register int n; + + for(n=0; n<=imm_fs; n++) + WriteNibble(d++, r[n]); +} + + +/*--------------------------------------------------------------------------- + Private functions: instruction fetch/immediate register load + ---------------------------------------------------------------------------*/ + +/* Read two nibbles in two-complement form, starting from pc */ +static Address Get2Nibbles2C(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4); + + return (v & 0x80) ? v - 0x100 : v; +} + +/* Read three nibbles in two-complement form, starting from pc */ +static Address Get3Nibbles2C(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4) | + ((Address)GetNibble(pc+2) << 8); + + return (v & 0x800) ? v - 0x1000 : v; +} + +/* Read four nibbles in two-complement form, starting from pc */ +static Address Get4Nibbles2C(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4) | + ((Address)GetNibble(pc+2) << 8) | ((Address)GetNibble(pc+3) << 12); + + return (v & 0x8000) ? v - 0x10000 : v; +} + +/* Read four nibbles in absolute form, starting from pc */ +static Address Get5NibblesAbs(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4) | + ((Address)GetNibble(pc+2) << 8) | ((Address)GetNibble(pc+3) << 12) | + ((Address)GetNibble(pc+4) << 16); + + return v; +} + +/* Fetch the lower 'n' nibbles of the D register pointed by 'd' from the + current instruction body +*/ +void FetchD(Address *d, register int n) +{ + register Address mask = ADDRESS_MASK; + register Address v = 0x00000; + register int shift = 0; + register int i; + + for(i=0; i= NIBBLE_PER_REGISTER) p=0; + } +} + + +/*--------------------------------------------------------------------------- + Private functions: P register setting + ---------------------------------------------------------------------------*/ +void SetP(Nibble n) +{ + cpu_status.P = n; + + cpu_status.fs_idx_lo[FS_P] = n; + cpu_status.fs_idx_hi[FS_P] = n; + cpu_status.fs_idx_hi[FS_WP] = n; +} + + +/*--------------------------------------------------------------------------- + Private functions: DataRegister tests + ---------------------------------------------------------------------------*/ + +/* ?r=s */ +static void TestRREq(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register const Nibble *const s = reg_pair_1[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRREq"); + for(n=lo; n<=hi; n++) + if(r[n] != s[n]) { cpu_status.carry = 0; return; }; + + cpu_status.carry = 1; +} + +/* ?r=0 */ +static void TestRZ(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRZ"); + for(n=lo; n<=hi; n++) + if(r[n] != (Nibble)0) { cpu_status.carry = 0; return; }; + + cpu_status.carry = 1; +} + +/* ?r#s */ +static void TestRRNe(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register const Nibble *const s = reg_pair_1[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRRNe"); + for(n=lo; n<=hi; n++) + if(r[n] != s[n]) { cpu_status.carry = 1; return; }; + + cpu_status.carry = 0; +} + +/* ?r#0 */ +static void TestRNZ(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRNZ"); + for(n=lo; n<=hi; n++) + if(r[n] != (Nibble)0) { cpu_status.carry = 1; return; }; + + cpu_status.carry = 0; +} + +/* ?r>s */ +static void TestRRGt(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register const Nibble *const s = reg_pair_1[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRRGt"); + for(n=hi; n>=lo; n--) + { + if(r[n] > s[n]) { cpu_status.carry = 1; return; }; + if(r[n] < s[n]) { cpu_status.carry = 0; return; }; + } + + cpu_status.carry = 0; +} + +/* ?r>=s */ +static void TestRRGe(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register const Nibble *const s = reg_pair_1[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRRGe"); + for(n=hi; n>=lo; n--) + { + if(r[n] > s[n]) { cpu_status.carry = 1; return; }; + if(r[n] < s[n]) { cpu_status.carry = 0; return; }; + } + + cpu_status.carry = 1; +} + +/* ?r=lo; n--) + { + if(r[n] < s[n]) { cpu_status.carry = 1; return; }; + if(r[n] > s[n]) { cpu_status.carry = 0; return; }; + } + + cpu_status.carry = 0; +} + +/* ?r<=s */ +static void TestRRLe(int rp, int fs) +{ + register const Nibble *const r = reg_pair_0[rp]; + register const Nibble *const s = reg_pair_1[rp]; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "TestRRLe"); + for(n=hi; n>=lo; n--) + { + if(r[n] < s[n]) { cpu_status.carry = 1; return; }; + if(r[n] > s[n]) { cpu_status.carry = 0; return; }; + } + + cpu_status.carry = 1; +} + + +/*--------------------------------------------------------------------------- + Private functions: DataRegister operations + ---------------------------------------------------------------------------*/ + +/* r=r+r */ +static void AddRR(register Nibble *d, + register const Nibble *a, register const Nibble *b, int fs) +{ + register int carry; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + register int s; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "AddRR"); + carry = 0; + + if(cpu_status.hexmode) + { + for(n=lo; n<=hi; n++) + { + s = a[n] + b[n] + carry; + + d[n] = (Nibble)(s & NIBBLE_MASK); + carry = ((s & ~NIBBLE_MASK) != 0); + } + } + + else + { + for(n=lo; n<=hi; n++) + { + s = a[n] + b[n] + carry; + d[n] = dec_sum[s]; + carry = dec_carry[s]; + } + } + + cpu_status.carry = carry; +} + +/* r=r+1 */ +static void IncrR(register Nibble *d, int fs) +{ + register int carry; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + register int s; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "IncrR"); + carry = 1; + + if(cpu_status.hexmode) + { + for(n=lo; n<=hi; n++) + { + s = d[n] + carry; + + d[n] = (Nibble)(s & NIBBLE_MASK); + carry = ((s & ~NIBBLE_MASK) != 0); + } + } + + else + { + for(n=lo; n<=hi; n++) + { + s = d[n] + carry; + d[n] = dec_sum[s]; + carry = dec_carry[s]; + } + } + + cpu_status.carry = carry; +} + +/* r=r-r */ +static void SubRR(register Nibble *d, + register Nibble *a, register Nibble *b, int fs) +{ + register int carry; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + register int s; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "SubRR"); + carry = 0; + + if(cpu_status.hexmode) + { + for(n=lo; n<=hi; n++) + { + s = a[n] - b[n] - carry; + + d[n] = (Nibble)(s & NIBBLE_MASK); + carry = ((s & ~NIBBLE_MASK) != 0); + } + } + + else + { + for(n=lo; n<=hi; n++) + { + s = a[n] - b[n] - carry; + d[n] = dec_sub[s]; + carry = dec_borrow[s]; + } + } + + cpu_status.carry = carry; +} + +/* r=r-1 */ +static void DecrR(register Nibble *d, int fs) +{ + register int carry; + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + register int s; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "DecrR"); + carry = 1; + + if(cpu_status.hexmode) + { + for(n=lo; n<=hi; n++) + { + s = d[n] - carry; + + d[n] = (Nibble)(s & NIBBLE_MASK); + carry = ((s & ~NIBBLE_MASK) != 0); + } + } + + else + { + for(n=lo; n<=hi; n++) + { + s = d[n] - carry; + d[n] = dec_sub[s]; + carry = dec_borrow[s]; + } + } + + cpu_status.carry = carry; +} + +/* r=0 */ +static void ClearR(register Nibble *d, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ClearR"); + for(n=lo; n<=hi; n++) + { + d[n] = (Nibble)0; + } +} + +/* r=r */ +static void CopyRR(register Nibble *d, register Nibble *s, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "CopyRR"); + for(n=lo; n<=hi; n++) + { + d[n] = s[n]; + } +} + +/* rrEX */ +static void ExchRR(register Nibble *d, register Nibble *s, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register Nibble t; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExchRR"); + for(n=lo; n<=hi; n++) + { + t = d[n]; d[n] = s[n]; s[n] = t; + } +} + +/* rSL */ +static void ShiftLeftR(register Nibble *d, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ShiftLeftR"); + for(n=hi; n>lo; n--) + d[n] = d[n-1]; + + d[lo] = (Nibble)0; +} + +/* rSR */ +static void ShiftRightR(register Nibble *d, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ShiftRightR"); + if(d[lo] != (Nibble)0) cpu_status.HST |= HST_SB_MASK; + + for(n=lo; n>= 1; + d[n] |= ((d[n+1] & nibble_bit_mask[0]) ? nibble_bit_mask[3] : 0); + } + + d[hi] >>= 1; +} + +/* rSLC */ +static void ShiftLeftCircR(register Nibble *d, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register Nibble s; + register int n; + + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ShiftLeftCircR"); + s = d[hi]; + for(n=hi; n>lo; n--) + d[n] = d[n-1]; + + d[lo] = s; +} + +/* rSRC */ +static void ShiftRightCircR(register Nibble *d, int fs) +{ + register int lo = cpu_status.fs_idx_lo[fs]; + register int hi = cpu_status.fs_idx_hi[fs]; + register Nibble s; + register int n; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ShiftRightCircR"); + if((s=d[lo]) != (Nibble)0) cpu_status.HST |= HST_SB_MASK; + + for(n=lo; n cpu_status.D0); + cpu_status.D0 = ta; + break; + + case 9: + /* D0=(2) nn */ + FetchD(&cpu_status.D0, 2); + break; + + case 0xA: + /* D0=(4) nn */ + FetchD(&cpu_status.D0, 4); + break; + + case 0xB: + /* D0=(5) nn */ + FetchD(&cpu_status.D0, 5); + break; + + case 0xC: + /* D1=D1-(n+1) */ + n = GetNibble(cpu_status.PC++); + ta = (cpu_status.D1 - n - 1) & ADDRESS_MASK; + cpu_status.carry = (ta > cpu_status.D1); + cpu_status.D1 = ta; + break; + + case 0xD: + /* D1=(2) nn */ + FetchD(&cpu_status.D1, 2); + break; + + case 0xE: + /* D1=(4) nn */ + FetchD(&cpu_status.D1, 4); + break; + + case 0xF: + /* D1=(5) nn */ + FetchD(&cpu_status.D1, 5); + break; + + default: + /* Unknown opcode */ + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd; + ChfSignal(); + break; + } +} + +/* Instruction Group_808 */ +static void ExecGroup_808(void) +{ + Nibble n = GetNibble(cpu_status.PC++); + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_808"); + switch(n) + { + case 0: /* INTON */ + ExecINTON(); + break; + + case 1: /* RSI */ + ExecRSI(); + break; + + case 2: /* LA(m) n..n */ + FetchR(cpu_status.A, GetNibble(cpu_status.PC++)); + break; + + case 3: /* BUSCB */ + ExecBUSCB(); + break; + + case 4: /* ABIT=0 d */ + ExecBIT0(cpu_status.A, GetNibble(cpu_status.PC++)); + break; + + case 5: /* ABIT=1 d */ + ExecBIT1(cpu_status.A, GetNibble(cpu_status.PC++)); + break; + + case 6: /* ?ABIT=0 d */ + TestBIT0(cpu_status.A, GetNibble(cpu_status.PC++)); + ExecGOYES_RTNYES(); + break; + + case 7: /* ?ABIT=1 d */ + TestBIT1(cpu_status.A, GetNibble(cpu_status.PC++)); + ExecGOYES_RTNYES(); + break; + + case 8: /* CBIT=0 d */ + ExecBIT0(cpu_status.C, GetNibble(cpu_status.PC++)); + break; + + case 9: /* CBIT=1 d */ + ExecBIT1(cpu_status.C, GetNibble(cpu_status.PC++)); + break; + + case 0xA: /* ?CBIT=0 d */ + TestBIT0(cpu_status.C, GetNibble(cpu_status.PC++)); + ExecGOYES_RTNYES(); + break; + + case 0xB: /* ?CBIT=1 d */ + TestBIT1(cpu_status.C, GetNibble(cpu_status.PC++)); + ExecGOYES_RTNYES(); + break; + + case 0xC: /* PC=(A) */ + cpu_status.PC = Get5NibblesAbs(R2Addr(cpu_status.A)); + break; + + case 0xD: + /* BUSCD */ + ExecBUSCD(); + break; + + case 0xE: + /* PC=(C) */ + cpu_status.PC = Get5NibblesAbs(R2Addr(cpu_status.C)); + break; + + case 0xF: + /* INTOFF */ + ExecINTOFF(); + break; + + default: + /* Unknown opcode */ + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd; + ChfSignal(); + break; + } +} + +/* Instruction Group_80 */ +static void ExecGroup_80(void) +{ + Nibble n = GetNibble(cpu_status.PC++); + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_80"); + switch(n) + { + case 0: /* OUT=CS */ + ExecOUTCS(); + break; + + case 1: /* OUT=C */ + ExecOUTC(); + break; + + case 2: /* A=IN */ + ExecIN(cpu_status.A); + break; + + case 3: /* C=IN */ + ExecIN(cpu_status.C); + break; + + case 4: /* UNCNFG */ + ModUnconfig(R2Addr(cpu_status.C)); + break; + + case 5: /* CONFIG */ + ModConfig(R2Addr(cpu_status.C)); + break; + + case 6: /* C=ID */ + Addr2R(cpu_status.C, ModGetID()); + break; + + case 7: /* SHUTDN */ + ExecSHUTDN(); + break; + + case 8: /* Group 808 */ + ExecGroup_808(); + break; + + case 9: /* C+P+1 */ + AddRImm(cpu_status.C, FS_A, cpu_status.P); + break; + + case 0xA: /* RESET */ + ModReset(); + break; + + case 0xB: /* BUSCC */ + ExecBUSCC(); + break; + + case 0xE: /* SREQ? */ + ExecSREQ(); + break; + + case 0xC: /* C=P n */ + cpu_status.C[(int)GetNibble(cpu_status.PC++)] = cpu_status.P; + break; + + case 0xD: /* P=C n */ + SetP(cpu_status.C[(int)GetNibble(cpu_status.PC++)]); + break; + + case 0xF: /* CPEX */ + { + Nibble t; + n = GetNibble(cpu_status.PC++); + t = cpu_status.P; + SetP(cpu_status.C[(int)n]); + cpu_status.C[(int)n] = t; + break; + } + + default: + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd; + ChfSignal(); + break; + } +} + +/* Special functions Group_81 */ +static void ExecSpecialGroup_81(int rp) +{ + Nibble n, f, m; + int rn, ac; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecSpecialGroup_81"); + switch(rp) + { + case 0: /* r=r+-CON fs, d */ + f = GetNibble(cpu_status.PC++); + n = GetNibble(cpu_status.PC++); + m = GetNibble(cpu_status.PC++); + rp = GetRP(n); + + if(GetAS(n)) /* Subtract */ + SubRImm(reg_pair_0[rp], f, m); + else /* Add */ + AddRImm(reg_pair_0[rp], f, m); + break; + + case 1: /* rSRB.f fs */ + f = GetNibble(cpu_status.PC++); + n = GetNibble(cpu_status.PC++); + rp = GetRP(n); + ShiftRightBitR(reg_pair_0[rp], f); + break; + + case 2: /* Rn=r.F fs, r=R0.F fs, rRnEX.F fs */ + f = GetNibble(cpu_status.PC++); + n = GetNibble(cpu_status.PC++); + m = GetNibble(cpu_status.PC++); + rn = GetRn(m); + ac = GetAC(m); + + switch(n) + { + case 0: /* Rn=r.F fs */ + CopyRR(cpu_status.R[rn], (ac ? cpu_status.C : cpu_status.A), f); + break; + + case 1: /* r=R0.F fs */ + CopyRR((ac ? cpu_status.C : cpu_status.A), cpu_status.R[rn], f); + break; + + case 2: /* rRnEX.F fs */ + ExchRR((ac ? cpu_status.C : cpu_status.A), cpu_status.R[rn], f); + break; + + default: + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd; + ChfSignal(); + break; + } + break; + + case 3: /* Group 81B */ + switch(n = GetNibble(cpu_status.PC++)) + { + case 2: /* PC=A */ + cpu_status.PC = R2Addr(cpu_status.A); + break; + + case 3: /* PC=C */ + cpu_status.PC = R2Addr(cpu_status.C); + break; + + case 4: /* A=PC */ + Addr2R(cpu_status.A, cpu_status.PC); + break; + + case 5: /* C=PC */ + Addr2R(cpu_status.C, cpu_status.PC); + break; + + case 6: /* APCEX */ + { + Address t; + t = R2Addr(cpu_status.A); + Addr2R(cpu_status.A, cpu_status.PC); + cpu_status.PC = t; + break; + } + + case 7: /* CPCEX */ + { + Address t; + t = R2Addr(cpu_status.C); + Addr2R(cpu_status.C, cpu_status.PC); + cpu_status.PC = t; + break; + } + + default: + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd; + ChfSignal(); + break; + } + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Register_Pair" ChfEnd; + ChfSignal(); + break; + } +} + + +/* Instruction Group_8 */ +static void ExecGroup_8(void) +{ + Nibble n = GetNibble(cpu_status.PC++); + Address addr; + int oc, rp; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_8"); + switch(n) + { + case 0: + ExecGroup_80(); + break; + + case 1: /* rSLC, rSRC, rSRB, Special Group_81 */ + n = GetNibble(cpu_status.PC++); + oc = GetOC_1(n); + rp = GetRP(n); + + switch(oc) + { + case 0: /* rSLC */ + ShiftLeftCircR(reg_pair_0[rp], FS_W); + break; + + case 1: /* rSRC */ + ShiftRightCircR(reg_pair_0[rp], FS_W); + break; + + case 2: /* Special Group_81 */ + ExecSpecialGroup_81(rp); + break; + + case 3: /* rSRB */ + ShiftRightBitR(reg_pair_0[rp], FS_W); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + break; + + case 2: /* CLRHSn */ + cpu_status.HST &= ~GetNibble(cpu_status.PC++); + break; + + case 3: /* ?HS=0 */ + n = GetNibble(cpu_status.PC++); + cpu_status.carry = ((cpu_status.HST & n) == 0); + ExecGOYES_RTNYES(); + break; + + case 4: /* ST=0 n */ + cpu_status.ST &= ~st_bit_mask[(int)GetNibble(cpu_status.PC++)]; + break; + + case 5: /* ST=1 n */ + cpu_status.ST |= st_bit_mask[(int)GetNibble(cpu_status.PC++)]; + break; + + case 6: /* ?ST=0 n */ + cpu_status.carry = + ((cpu_status.ST & st_bit_mask[(int)GetNibble(cpu_status.PC++)]) == 0); + ExecGOYES_RTNYES(); + break; + + case 7: /* ?ST=1 n */ + cpu_status.carry = + ((cpu_status.ST & st_bit_mask[(int)GetNibble(cpu_status.PC++)]) != 0); + ExecGOYES_RTNYES(); + break; + + case 8: /* ?P#n */ + cpu_status.carry = (cpu_status.P != GetNibble(cpu_status.PC++)); + ExecGOYES_RTNYES(); + break; + + case 9: /* ?P=n */ + cpu_status.carry = (cpu_status.P == GetNibble(cpu_status.PC++)); + ExecGOYES_RTNYES(); + break; + + case 0xA: /* Test */ + ExecTest_8A(); + break; + + case 0xB: /* Test */ + ExecTest_8B(); + break; + + case 0xC: /* GOLONG */ + addr = Get4Nibbles2C(cpu_status.PC); + cpu_status.PC += addr; + break; + + case 0xD: + /* GOVLNG */ + cpu_status.PC = Get5NibblesAbs(cpu_status.PC); + break; + + case 0xE: + /* GOSUBL */ + addr = Get4Nibbles2C(cpu_status.PC); + cpu_status.PC += 4; + PushRSTK(cpu_status.PC); + cpu_status.PC += addr; + break; + + case 0xF: + /* GOSBVL */ + PushRSTK(cpu_status.PC + 5); + cpu_status.PC = Get5NibblesAbs(cpu_status.PC); + break; + + default: + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd; + ChfSignal(); + break; + } +} + + +/*--------------------------------------------------------------------------- + Private functions: dump + ---------------------------------------------------------------------------*/ + +const char *DumpR(Nibble *r) +{ + static char b[NIBBLE_PER_REGISTER+1]; + static const char hex_char[NIBBLE_PER_REGISTER] = "0123456789ABCDEF"; + int n; + + for(n=0; n 0) + if(--cpu_status.halt == 0) + { + debug0(DEBUG_C_INT, CPU_I_RUN); + + /* CPU must actually be awoken: call CpuWake() */ + CpuWake(); + } + + return cpu_status.halt; + +#endif +} + + +/* .+ + +.title : CpuHaltAllowed +.kind : C function +.creation : 7-Nov-2000 +.description : + This function return a non-zero value if CpuHaltRequest() + is allowed, zero otherwise. + +.call : + s = CpuHaltRequest(); +.input : + void +.output : + int s, non-zero if CpuHaltRequest() is allowed, 0 otherwise +.status_codes : + CPU_I_CALLED +.notes : + 3.13, 7-Nov-2000, creation + +*/ +int CpuHaltAllowed(void) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_INT, CPU_I_CALLED, "CpuHaltAllowed"); + +#ifdef CPU_SPIN_SHUTDN + return 0; + +#else + return 1; + +#endif +} + + +/* .+ + +.title : DumpCpuStatus +.kind : C function +.creation : 3-Feb-1998 +.description : + This function dumps the current CPU status into the string buffer 'ob'. + +.call : + DumpCpuStatus(ob); +.input : + void +.output : + char ob[DUMP_CPU_STATUS_OB_SIZE]; +.status_codes : + * +.notes : + 1.1, 3-Feb-1998, creation + +.- */ +void DumpCpuStatus(char ob[DUMP_CPU_STATUS_OB_SIZE]) +{ + static const char *work_n[N_WORKING_REGISTER] = { "A", "B", "C", "D" }; + char dob[DISASSEMBLE_OB_SIZE]; + int n; + + /* Dump PC and current instruction */ + (void)Disassemble(cpu_status.PC, dob); + sprintf(ob, "%s\n\n", dob); + ob += strlen(ob); + + /* Dump A, B, C, D */ + for(n=0; n>2) + + GetOC_2(f, o) returns the long operation code from the given + nibbles (f bit 3, o bits 3..2) + + GetOC_3b(o) returns the long operation code from the given + nibble (bits 2..0) + + GetRP(o) returns the register-pair identifier from the given + nibble (bits 1..0) + + GetRn(r) returns the R register index from the given nibble + (bits 2..0) + + GetAC(r) returns the A/C register flag from the given nibble + (bit 3) + =0: register A + !=0: register C + + GetAS(r) returns the add/subtract flag from the given nibble + (bit 3) + =0: add + !=0: subtract +*/ +#define GetFS(f) ((f) & 0x7) +#define GetImmFS(o) ((o) & 0x8) +#define GetOC_1(o) (((o) & 0xC)>>2) +#define GetOC_2(f, o) ((((f) & 0x8)>>1) | (((o) & 0xC)>>2)) +#define GetOC_3b(o) ((o) & 0x7) +#define GetRP(o) ((o) & 0x3) +#define GetRn(r) ((r) & 0x7) +#define GetAC(r) ((r) & 0x8) +#define GetAS(r) ((r) & 0x8) + + +/* Field selector codes */ +#define FS_P 0 +#define FS_WP 1 +#define FS_XS 2 +#define FS_X 3 +#define FS_S 4 +#define FS_M 5 +#define FS_B 6 +#define FS_W 7 +#define FS_A 15 +#define N_FS 16 /* Total # of FS codes */ + + +/* Register pair codes */ +#define RP_AB 0 +#define RP_BC 1 +#define RP_CA 2 +#define RP_DC 3 +#define N_RP 4 /* Total # of RP codes */ + + +/* Masks */ +#define NIBBLE_MASK ((Nibble)0xF) +#define ADDRESS_MASK ((Address)0xFFFFF) + +#define CLRST_MASK ((ProgramStatusRegister)0xF000) +#define D_S_MASK ((Address)0xF0000) +#define RETURN_SP_MASK 0x7 + + +typedef int1 Bit; +typedef int4 Nibble; +typedef int20 Address; +typedef int12 OutputRegister; +typedef int16 InputRegister; +typedef int16 ProgramStatusRegister; +typedef Nibble DataRegister[NIBBLE_PER_REGISTER]; + +/* The XAddress data type holds extended addresses used to access Port 2 */ +typedef int32 XAddress; + +enum IntRequest +{ + INT_REQUEST_NONE, + INT_REQUEST_IRQ, + INT_REQUEST_NMI +}; + +struct CpuStatus +{ + DataRegister work[N_WORKING_REGISTER]; +#define A work[0] +#define B work[1] +#define C work[2] +#define D work[3] + + DataRegister R[N_SCRATCH_REGISTER_ALL]; +#define R0 R[0] +#define R1 R[1] +#define R2 R[2] +#define R3 R[3] +#define R4 R[4] + + Address DAT[N_DATA_POINTER_REGISTER]; +#define D0 DAT[0] +#define D1 DAT[1] + + Nibble P; + Address PC; + InputRegister IN; + OutputRegister OUT; + ProgramStatusRegister ST; + + Nibble HST; +#define HST_MP_MASK 0x08 +#define HST_SR_MASK 0x04 +#define HST_SB_MASK 0x02 +#define HST_XM_MASK 0x01 + + Address return_stack[RETURN_STACK_SIZE]; + int return_sp; + + int fs_idx_lo[N_FS]; + int fs_idx_hi[N_FS]; + int hexmode; /* DEC/HEX mode, 1=HEX */ + int carry; /* Carry bit 1=set */ + int shutdn; /* SHUTDN flag, 1=executed */ + int halt; /* 3.13: # of pending Halt */ + int int_enable; /* Int. enable, 1=enabled */ + int int_service; /* Int. service, 1=service */ + enum IntRequest int_pending; /* Pending interrupt request */ + + /* 3.13: inner_loop_max gives the upper limit of the CPU speed if the + compile-time option REAL_CPU_SPEED is defined. When the CPU is reset + it has the default value INNER_LOOP_MAX, that should be close to the + real cpu speed (~4MHz). + */ + int inner_loop; /* Inner loop multiplier */ + int inner_loop_max; /* Max value of inner_loop */ +#define INNER_LOOP_MAX 26 +#define INNER_LOOP_MED 13 +#define INNER_LOOP_MIN 2 + +#ifdef CPU_SPIN_LOOP + int reset_req; /* Reset req. after shutdn */ +#endif +}; + +enum ExitOption /* 2.1: EmulatorExit() option */ +{ + IMMEDIATE_EXIT, + SAVE_AND_EXIT +}; + + +/*--------------------------------------------------------------------------- + Global variables + ---------------------------------------------------------------------------*/ + +extern struct CpuStatus cpu_status; + + +/*--------------------------------------------------------------------------- + Chf condition codes + ---------------------------------------------------------------------------*/ + +#define CPU_I_CALLED 101 /* Function %s called */ +#define CPU_I_EXECUTING 102 /* Executing @PC %X */ +#define CPU_I_SHUTDN 103 /* Shutdown */ +#define CPU_I_WAKE 104 /* Wake */ +#define CPU_I_INT 105 /* %s request accepted */ +#define CPU_I_INT_PENDING 106 /* %s request pending */ +#define CPU_I_RTI_LOOP 107 /* RTI loop to service %s */ +#define CPU_I_RTI_END 108 /* RTI returning */ +#define CPU_I_INTON 109 /* INTON servicing %s */ +#define CPU_I_REVISION 110 /* CPU emulation revision: %s */ +#define CPU_I_TIMER1_EX 111 /* Timer 1 expired; ctrl=%x */ +#define CPU_I_TIMER2_EX 112 /* Timer 1 expired; ctrl=%x */ +#define CPU_I_EMULATOR_INT 113 /* Emulator interrupt req. detected */ +#define CPU_I_TIMER_ST 114 /* 3.1: Timer %s st: ctrl %x, val %x */ +#define CPU_I_TIMER_EXP 115 /* 3.1: Timer %s expiration %d ms */ +#define CPU_I_IDLE_X_LOOP 116 /* 3.1: Start idle loop, t/out %d ms */ +#define CPU_I_ELAPSED 117 /* 3.1: Spent %d us in idle loop */ +#define CPU_I_HALT 118 /* 3.13: CPU halted */ +#define CPU_I_RUN 119 /* 3.13: CPU running */ +#define CPU_W_RESETTING 201 /* Resetting CPU */ +#define CPU_W_BAD_MONITOR_CMD 202 /* Bad monitor command: %s */ +#define CPU_E_BAD_OPCODE 301 /* Bad opc. pc=%x, value=%x */ +#define CPU_E_SAVE 302 /* Can't save CPU status */ +#define CPU_E_NO_HALT 303 /* 3.13: Halt/Run not allowed */ +#define CPU_F_INTERR 401 /* Internal error %s */ +#define CPU_F_BAD_SHUTDN 402 /* Unexpected CPU shutdown */ + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +void CpuInit(void); +void CpuReset(void); +void CpuSave(void); +void OneStep(void); +void CpuIntRequest(enum IntRequest ireq); +void CpuWake(void); +void Emulator(void); +void EmulatorIntRequest(void); +void EmulatorInit(void); /* 2.1 */ +void EmulatorExit(enum ExitOption opt); /* 2.1 */ +int CpuHaltRequest(void); /* 3.13 */ +int CpuRunRequest(void); /* 3.13 */ +int CpuHaltAllowed(void); /* 3.13 */ + +Address Disassemble(Address pc, char ob[DISASSEMBLE_OB_SIZE]); +void DumpCpuStatus(char ob[DUMP_CPU_STATUS_OB_SIZE]); diff --git a/cpu.msf b/cpu.msf new file mode 100644 index 0000000..d6135b5 --- /dev/null +++ b/cpu.msf @@ -0,0 +1,64 @@ +$ .+ +$ .identifier : $Id: cpu.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HP48 emulator +$ .title : $RCSfile: cpu.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 13-Feb-1998 +$ .keywords : * +$ .description : +$ Message catalog source file for the CPU emulation modules. +$ .notes : +$ . $Log: cpu.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.13 2000/11/09 11:27:48 cibrario +$ . Revised to add file selection box GUI element, CPU halt/run +$ . requests and emulator's extended functions: +$ . +$ . - New messages: 118, 119, 303 +$ . +$ . Revision 3.8 2000/10/19 14:56:52 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 3.1 2000/09/20 13:44:30 cibrario +$ Revised to implement passive CPU shutdown: +$ - new messages 114, 115, 116, 117 +$ . +$ Revision 1.1 1998/02/18 11:50:36 cibrario +$ Initial revision +$ .- + +$set 1 +11 Cpu + +$set 11 +101 Function [%s] called +102 Executing @ PC[%05X] +103 CPU shutdown executed +104 CPU wake-up executed +105 %s request accepted +106 %s request pending +107 RTI loop to service %s +108 RTI returning +109 INTON servicing %s +110 CPU emulator [%s] +111 Timer1 expired CTRL[%01X] +112 Timer2 expired CTRL[%01X] +113 Emulator interrupt request detected +114 Timer[%s] Ctrl[%01X] Val[%08X] +115 Timer[%s] expires in [%d]d ms +116 Starting idle loop, timeout [%d]d ms +117 Spooling up after [%d]d us in idle loop +118 CPU halted by CpuHaltRequest() +119 CPU awoken by CpuRunRequest() +201 Can't restore CPU status from disk; resetting CPU +202 Monitor command syntax error [%s] +301 Bad opcode @ PC[%05X], N[%01X] +302 Can't save CPU status to disk +303 Halt/Run requests not allowed; #undef CPU_SPIN_SHUTDN +401 Internal error [%s] +402 Unexpected CPU shutdown diff --git a/custom.texi b/custom.texi new file mode 100644 index 0000000..021eee2 --- /dev/null +++ b/custom.texi @@ -0,0 +1,393 @@ +@c $Id: custom.texi,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + +@node Customizing saturn, The sutil Library, Command Line Options, Top +@chapter Customizing saturn +@cindex Customizing saturn + +The @code{saturn} program uses OSF/Motif widgets to make up its +user interface, and supports the standard X application +resource mechanism for customization; in addition, it is fully +internationalized and uses a message catalog to retrieve almost +all error and informational messages the user will ever see. + +Therefore, you can modify the ``look and feel'' of @code{saturn} +to fit your preferences to a great extent, without touching +the source code and/or rebuilding the executable. + +This chapter deals with advanced customization, that is, X resources, +message catalog, and so on, and does not discuss the command-line +options of @code{saturn}; see @ref{Command Line Options}, for more +information about command-line options. + +@menu +* Widget Tree:: +* Custom Resources:: +* Application Actions:: +* Message Catalog:: +* Environment Variables:: +* Customizing the Translation Table:: +* Customizing the Font Size:: +* Configuration Options:: +@end menu + + +@node Widget Tree, Custom Resources, Customizing saturn, Customizing saturn +@section Widget Tree +@cindex Widget Tree + +This is @code{saturn}'s widget tree; all widgets are standard Motif +widgets. See the Motif documentation for more information about +standard widget's resources. + +@table @code +@item main (XmMainWindow) +This is the emulator's main window, and is a child of the main +application shell. +@item *fsb (XmFileSelectionBox) +This is the file selection box that pops up when a fast load/save +operation is started. Notice the star; it is there because this +widget is wrapped in a dialog. +@item *error (XmMessageBox) +This is the message box that pops up when @code{saturn} wants to tell +something to the user, usually when an error occurs. Notice the star; it +is there because this widget is wrapped in a dialog. +@item main.@var{faceplate} (XmRowColumn) +This container contains all widgets of the calculator's faceplate. +The name of this widget, @var{faceplate}, depends on the value +of the custom resource @code{.face}; this way you can define +a different set of resources for each faceplate. +@item main.@var{faceplate}.kbd (XmForm) +This form contains all calculator's keys. +@item main.@var{faceplate}.kbd.@var{n} (XmForm) +Another @code{XmForm}, containing all widgets pertaining to calculator's +key number @var{n}. +@item main.@var{faceplate}.kbd.@var{n}.ul (XmLabel) +@itemx main.@var{faceplate}.kbd.@var{n}.ur (XmLabel) +@itemx main.@var{faceplate}.kbd.@var{n}.ll (XmLabel) +@itemx main.@var{faceplate}.kbd.@var{n}.lr (XmLabel) +These widgets contain, respectively, the upper-left, upper-right, +lower-left, and lower-right label of calculator's key number @var{n}. +@item main.@var{faceplate}.kbd.@var{n}.btn (XmToggleButton) +This widget represents calculator's key number @var{n}. +@item main.@var{faceplate}.frame (XmFrame) +This widget encloses the LCD display area. +@item main.@var{faceplate}.frame.lcd (XmDrawingArea) +This widget is the LCD display area of the emulated calculator. +@item main.@var{faceplate}.msg (XmTextField) +This text field is used by the emulator to display its most important +messages; for example, it is used to display the name of the +emulator's pseudo-terminal. +@end table + +In the list above: + +@table @var +@item faceplate +is the name of the active calculator's faceplate selected by the +@code{.face} custom resource. +@item n +is the ASCII decimal representation of a non-negative integer number, +that identifies a calculator's key uniquely; its value goes from +@code{0} to the value of the @var{faceplate}.@code{nKeys} resource minus +one, inclusive. +@end table + +@node Custom Resources, Application Actions, Widget Tree, Customizing saturn +@section Custom Resources +@cindex Custom Resources + +In addition to standard Motif resources, @code{saturn} has the +following custom resources: + +@table @code +@item main.@var{faceplate}.nKeys +This resource must translate into an integer, representing the number of +keys that faceplate @var{faceplate} has. +@item main.@var{faceplate}.kbd.@var{n}.btn.inOut +This resource associates calculator's key number @var{n} with a +combination of IN/OUT codes seen by the emulated CPU. It is a +string, and can be either: + +@table @code +@item @var{o}/@var{i} +When pressed, the key activates the bit(s) of the IN register specified +by the @var{i} bit @strong{mask}, when bit @strong{number} @var{o} is +set in the OUT register. Both @var{i} and @var{o} are hexadecimal +constants. For example, on the HP49 the calculator's function key +@code{F1} sets the IN bit mask @code{01} when OUT bit number @code{5} is set; +accordingly, the IN/OUT mapping of this key is @code{5/01}. +@item * +This special value uniquely identifies the ON/Cancel key. +@end table +@item *compoundString +This custom resource can be set for @code{XmLabel} and +@code{XmToggleButton} widgets only and, when set, overrides the standard +resource @code{labelString}. Its value is a string with the +following syntax: + +@example + @var{compoundString}: ([# @var{fontlist_tag}] @var{string})* +@end example + +Here, @var{string} is a sequence of ordinary characters, +excluding @code{#}, and @var{fontlist_tag} can be: +@table @code +@item # +Put a single @code{#} character in current compound string segment. +@item @var{space} +Create a new compound string segment using @code{XmFONTLIST_DEFAULT_TAG} +as tag. +@item @var{tag} +Create a new segment using @var{tag} as tag; @var{tag} can be +any single character, except @code{#} and @var{space}. +@end table + +Each segment is limited to @code{MAX_CS_SEGMENT_LEN} characters; longer +segments are silently truncated. + +The resource is scanned from left to right according to the syntax +described above. The result is a compound string (@code{XmString}) +that becomes the @code{labelString} resource of the target widget. +The original value of the @code{labelString} resource is discarded +when @code{compoundString} is defined. This resource allows you +to have more than one font in button labels. +@end table + +In the list above: + +@table @var +@item faceplate +is the name of the calculator's faceplate selected by the +@code{.face} custom resource. +@item n +is the ASCII decimal representation of a non-negative integer number +that uniquely identifies a calculator's key; its value goes from +@code{0} to the value of the @var{faceplate}.@code{nKeys} resource minus +one, inclusive. +@end table + +Notice also that all command-line options are mapped into custom +top-level application resources, too; in this case, the name of the +resource is the same as the name of the option. For example, +the @code{-face} command-line option is mapped to the custom +resource @code{.face}. + +@node Application Actions, Message Catalog, Custom Resources, Customizing saturn +@section Application Actions +@cindex Application Actions + +The emulator installs the application actions listed below: + +@table @code +@item kbdKeyPress +@itemx kbdKeyRelease +Both actions accept one string as argument; the string represents an +IN/OUT mapping with the same syntax already described for the +@code{inOut} custom resource. These functions command the emulation of +a key press and key release event, respectively. +@end table + +These actions are useful to define keyboard shortcuts. For example, on +the HP49 the calculator's function key @code{F1} sets the IN bit mask +@code{01} when OUT bit number @code{5} is set; accordingly, the IN/OUT +mapping of this key is @code{5/01}. + +If you want to define the X keysym @code{F1} to act as a shortcut for +this calculator's key, simply add the following two lines to the +@code{translations} resource of your faceplate: + +@example +... +F1: kbdKeyPress(5/01) \n\ +F1: kbdKeyRelease(5/01) \n\ +... +@end example + + +@node Message Catalog, Environment Variables, Application Actions, Customizing saturn +@section Message Catalog +@cindex Message Catalog + +The @code{saturn} emulator opens the message catalog @code{saturn.cat} +during startup, and retrieves all its messages from there. Internally, +@code{saturn} uses a pair of integers to uniquely identify a message. + +By default, that is, when the @code{NLSPATH} environment variable is not +set, @code{saturn} first attempts to locate the message catalog in the +system's default location of such files; if this first attempt fails, +@code{saturn} prints a warning and generates an alternate catalog name +using the directory name found in @code{argv[0]}; if even this second +attempt fails, @code{saturn} tries again using the @code{C} locale +instead of the current one. If all attempts fail, @code{saturn} +terminated. + +The @code{.msf} files found in the source distribution of @code{saturn} +list all message codes and translate them into human-readable english +messages; they can be used as a starting point to prepare message +catalogs for additional languages. See the documentation of +@code{gencat}, for more information about how to process them to +generate a @code{.cat} file. + + +@node Environment Variables, Customizing the Translation Table, Message Catalog, Customizing saturn +@section Environment Variables +@cindex Environment Variables + +The @code{saturn} emulator looks at the following environment variables: + +@table @code +@item NLSPATH +@itemx LC_ALL +@itemx LC_MESSAGES +@itemx LANG +These environment variables are used to locate the @code{saturn}'s +message catalog, and to specialize the load paths of application +resources; see the documentation of @code{catopen()} and +@code{XtResolvePathname()}, for more information. + +@item DISPLAY +This variable contains the default X display name used by @code{saturn}. + +@item XENVIRONMENT +This variable is used to locate the per-host user environment +resources; see the documentation of @code{XtDisplayInitialize()}, for +more information. + +@item XUSERFILESEARCHPATH +@itemx XAPPLRESDIR +These variables control the loading process of the user's application +resource file; see the documentation of @code{XtDisplayInitialize()}, for +more information. + +@item XFILESEARCHPATH +This variable controls the loading process of the application class +resource file; see the documentation of @code{XtDisplayInitialize()}, for +more information. +@end table + + +@node Customizing the Translation Table, Customizing the Font Size, Environment Variables, Customizing saturn +@section Customizing the Translation Table +@cindex Customizing the Translation Table + +As said before, all widgets in a given faceplate have their own +translation table; this table is useful to define keyboard +shortcuts. The default application resource file distributed +with the emulator, @file{Saturn.ad}, already contains a +predefined set of shortcuts; to customize it, proceed +as follows: + +@itemize @bullet +@item +Locate the translation table of the faceplate you are interested in. +For example, the predefined HP49's faceplate name is @code{hp49}; the X +resource specifier of its base translation table is: + +@example +*hp49*baseTranslations +@end example + +@item +This resource has two lines of text for each key on the real keyboard +you want to map on the emulated keyboard; for example, these lines map +the @code{0} keypad key to the @code{0} key of the calculator: + +@example +KP_0: kbdKeyPress(3/01) \n\ +KP_0: kbdKeyRelease(3/01) \n\ +@end example + +@item +Here, @code{KP_0} is the name of the X keysym (representing a keyboard +key or a combination of keys) that is currently mapped to the @code{0} +key (@code{3/01} are the OUT/IN bit masks of the @code{0} key on the +calculator, reflecting its wiring on the calculator's keyboard). To +change the mapping to another key, simply replace the old X keysym name +with the new one. For example, if you want to remap the @code{F8} key +to the @code{0} key, update the lines above as follows: + +@example +F8: kbdKeyPress(3/01) \n\ +F8: kbdKeyRelease(3/01) \n\ +@end example + +If you want to keep the existing mapping, too, duplicate the existing +lines and change the X keysym on the duplicate only. + +@item +To get the keysym name of a given key you can use, for example, the +@code{xev} utility: when you start it, it opens a small window on your +screen and starts dumping on stdout all X events that window receives. +Give the focus to the @code{xev} window, press the key you are +interested in and look at its output; you should see something like: + +@example +KeyPress event, serial 22, synthetic NO, window 0x4800001, + root 0x26, subw 0x0, time 18446744071995733347, (77,-9), + root:(123,37), + state 0x1, keycode 34 (keysym 0x7b, braceleft), same_screen YES, + XLookupString gives 1 characters: "@{" +@end example + +Here, I pressed the @code{@{} key, and @code{xev} is telling me that its +keysym name is @code{braceleft}. Use that name if you want to put the +@code{@{} key in translations. + +@item +Be careful with continuation characters: each line of the translation +table resource except the last one must end with @code{\n\} + +@item +Keep in mind that this is a simple example; actually, since +@code{saturn} leverages the standard X Toolkit translation table parser +and translator, it accepts the @strong{full} translation table syntax +described in MIT documents, that is much more complex and powerful than +this. + +@end itemize + + +@node Customizing the Font Size, Configuration Options, Customizing the Translation Table, Customizing saturn +@section Customizing the Font Size +@cindex Customizing the Font Size + +The simplest way to change the size of the @code{saturn}'s main window +is to change the size of the fonts it uses. To do this, locate the +@code{fontList} resources of the faceplate you want to modify in your +application resource file. For example, the default application +resource file sets the following resources for the @code{hp49} +faceplate: + +@example +*hp49.kbd*fontList: *helvetica-*-r-*-*-12-*,*symbol-*-*-*-*-12-*=S +*hp49.kbd*btn.fontList: *helvetica-*-r-*-*-14-*,*symbol-*-*-*-*-14-*=S +@end example + +Fonts are selected with their standard XLFD font designators; +in the above lines, @code{12} and @code{14} are the @code{pxlsz} specifiers. +Make them smaller to reduce the font size; make them bigger to +enlarge the font size. For example: + +@example +*hp49.kbd*fontList: *helvetica-*-r-*-*-8-*,*symbol-*-*-*-*-8-*=S +*hp49.kbd*btn.fontList: *helvetica-*-r-*-*-10-*,*symbol-*-*-*-*-10-*=S +@end example + +reduces the window size to about 400x700 pixels on my system. + +If this is not enough, you can define a brand new keyboard layout; +again, no need to touch the source code, only resources must be +updated. Hint: for each key, its @code{topPosition}, +@code{bottomPosition}, @code{leftPosition} and @code{rightPosition} +resources determine where the key is located on the keyboard faceplate. + + +@node Configuration Options, , Customizing the Font Size, Customizing saturn +@section Configuration Options +@cindex Configuration Options + +In addition to the run-time customization methods mentioned above, +@code{saturn} has build-time configuration options, too. See the +documentation embedded in the source file @code{config.h}, for more +information. Of course, you can change these options only if you build +@code{saturn} yourself from the source distribution. diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..93e8051 --- /dev/null +++ b/debug.c @@ -0,0 +1,127 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: debug.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: debug.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 28-Jan-1998 +.keywords : * +.description : + Debugging support. + +.include : debug.h + +.notes : + $Log: debug.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:32 cibrario + Added/Replaced GPL header + + Revision 1.1 1998/02/17 11:42:30 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: debug.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "debug.h" + +#define CHF_MODULE_ID DEBUG_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Static/Global variables + ---------------------------------------------------------------------------*/ + +#ifdef DEBUG +#ifdef DEBUG_LEVEL +int debug_level = DEBUG_LEVEL; +#else +int debug_level = 0; +#endif +#endif + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : SetDebugLevel +.kind : C function +.creation : 28-Jan-1998 +.description : + If runtime debug support is enabled (symbol DEBUG defined during executable + image build) this function updates the 'debug_level' flag, otherwise + it signals a condition and does nothing more. + +.call : + SetDebugLevel(new_level) +.input : + int new_level, new value of the debug_level flag +.output : + void +.status_codes : + DEBUG_W_NOT_SUPPORTED +.notes : + 1.1, 28-Jan-1998, creation + +.- */ +void SetDebugLevel(int new_level) +{ +#ifdef DEBUG + debug_level = new_level; +#else + ChfCondition DEBUG_W_NOT_SUPPORTED, CHF_WARNING ChfEnd; + ChfSignal(); +#endif +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..c13cc0e --- /dev/null +++ b/debug.h @@ -0,0 +1,173 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: debug.h,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: debug.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 19-Jan-1998 +.keywords : * +.description : + This header defines the following macros: + + - debug0(debug_class, condition_code) + - debug1(debug_class, condition_code, arg_1) + - debug2(debug_class, condition_code, arg_1, arg_2) + - debug3(debug_class, condition_code, arg_1, arg_2, arg_3) + + used throughout the source code for debugging purposes. + + If the DEBUG cpp symbol is defined, each invocation of these macros is + expanded into a block of code that, at runtime, checks if the global + variable 'debug_level' has set at least one of the bit 'debug_class' has + set (in other words, it checks if the current debug level enables at + least one of the classes to which the debugging condition belongs). + + If this condition is met, the code generates and immediately signals + the given condition code using the Chf facility, with severity CHF_INFO, + otherwise nothing is done. + + The arguments arg_1, arg_2, and arg_3 are used as additional arguments + of the condition. + + If the DEBUG cpp symbol is not defined, the macros are defined as a null + macros. + +.include : Chf.h + +.notes : + $Log: debug.h,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.13 2000/11/09 11:28:34 cibrario + Revised to add file selection box GUI element, CPU halt/run + requests and emulator's extended functions: + + - Added new debug class: DEBUG_C_X_FUNC + + Revision 3.10 2000/10/24 16:14:32 cibrario + Added/Replaced GPL header + + Revision 3.3 2000/09/26 15:20:40 cibrario + Revised to implement Flash ROM write access: + - Added new debug class: DEBUG_C_FLASH + + * Revision 2.7 2000/09/19 10:51:13 cibrario + * Added new debug class: DEBUG_C_MOD_CACHE + * + * Revision 2.5 2000/09/14 14:34:13 cibrario + * Added new debug class: DEBUG_C_SERIAL + * + * Revision 1.1 1998/02/18 11:54:33 cibrario + * Initial revision + * + +.- */ + +#ifdef DEBUG + +#define debug_preamble(debug_class, condition_code) \ + { \ + extern int debug_level; \ + if(debug_level & (debug_class)) \ + { \ + ChfCondition (condition_code), CHF_INFO + +#define debug_postamble \ + ChfEnd; \ + ChfSignal(); \ + } \ + } + +#define debug0(debug_class, condition_code) \ + debug_preamble(debug_class, condition_code) \ + debug_postamble + +#define debug1(debug_class, condition_code, arg_1) \ + debug_preamble(debug_class, condition_code), arg_1 \ + debug_postamble + +#define debug2(debug_class, condition_code, arg_1, arg_2) \ + debug_preamble(debug_class, condition_code), arg_1, arg_2 \ + debug_postamble + +#define debug3(debug_class, condition_code, arg_1, arg_2, arg_3) \ + debug_preamble(debug_class, condition_code), arg_1, arg_2, arg_3 \ + debug_postamble + +#else + +#define debug0(debug_class, condition_code) +#define debug1(debug_class, condition_code, arg_1) +#define debug2(debug_class, condition_code, arg_1, arg_2) +#define debug3(debug_class, condition_code, arg_1, arg_2, arg_3) + +#endif + + +/*--------------------------------------------------------------------------- + Debug classes + ---------------------------------------------------------------------------*/ + +#define DEBUG_C_TRACE 0x8000 /* Function Call trace */ +#define DEBUG_C_MODULES 0x4000 /* Modules configuration */ +#define DEBUG_C_DISPLAY 0x2000 /* Display activity */ +#define DEBUG_C_INT 0x1000 /* Interrupt activity */ +#define DEBUG_C_TIMERS 0x0800 /* Timers activity */ +#define DEBUG_C_SERIAL 0x0400 /* 2.5: Serial port activity */ +#define DEBUG_C_MOD_CACHE 0x0200 /* 2.7: Module cache */ +#define DEBUG_C_IMPLEMENTATION 0x0100 /* Feature implementation */ +#define DEBUG_C_FLASH 0x0080 /* 3.3: Flash ROM */ +#define DEBUG_C_X_FUNC 0x0040 /* 3.13: Extended functions */ +#define DEBUG_C_REVISION 0x0010 /* Revision information */ +#define DEBUG_C_X11 0x0001 /* X11 Interface */ + + +/*--------------------------------------------------------------------------- + Chf condition codes + ---------------------------------------------------------------------------*/ + +#define DEBUG_W_NOT_SUPPORTED 201 /* Debug not supported */ +#define DEBUG_W_BAD_CMD 202 /* Invalid command */ + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +void SetDebugLevel(int new_level); diff --git a/debug.msf b/debug.msf new file mode 100644 index 0000000..f590e7e --- /dev/null +++ b/debug.msf @@ -0,0 +1,29 @@ +$ .+ +$ .identifier : $Id: debug.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HP48 emulator +$ .title : $RCSfile: debug.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 13-Feb-1998 +$ .keywords : * +$ .description : +$ Message catalog source file for the debugging support modules. +$ .notes : +$ . $Log: debug.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.8 2000/10/19 14:55:10 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 1.1 1998/02/18 11:54:02 cibrario +$ Initial revision +$ .- + +$set 1 +30 Debug + +$set 30 +201 Debug not supported; Rebuild with -DDEBUG diff --git a/dis.c b/dis.c new file mode 100644 index 0000000..516cc6f --- /dev/null +++ b/dis.c @@ -0,0 +1,2407 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: dis.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: dis.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 26-Jan-1998 +.keywords : * +.description : + This module contains a disassembler for the instruction set of the + Saturn CPU. + + The syntax of the disassembled code conforms to the reference: + SASM.DOC by HP (HORN disk 4) + with some extensions from + Guide to the Saturn Processor Rev. 0.00f by Matthew Mastracci + + The disassembler is almost not table-driven, because its structure has + been kept as similar as possible to the actual CPU emulator code. + +.include : config.h, machdep.h, cpu.h, modules.h + +.notes : + $Log: dis.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:34 cibrario + Added/Replaced GPL header + + Revision 3.5 2000/10/02 09:44:12 cibrario + Linux support: + - gcc does not like array subscripts with type 'char', and it is right. + + Revision 3.1 2000/09/20 13:47:07 cibrario + Minor updates and fixes to avoid gcc compiler warnings on Solaris + when -ansi -pedantic -Wall options are selected. + + * Revision 1.2 2000/09/07 14:29:39 cibrario + * Bug fix: Incorrect use of strcpy() when disassembling "?HS=0\t%X" + * + * Revision 1.1 1998/02/13 14:05:30 cibrario + * Initial revision + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: dis.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include /* 3.1: strcpy(), strcat(), strlen() */ + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "debug.h" + +#define CHF_MODULE_ID CPU_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Private functions/macros/variables + ---------------------------------------------------------------------------*/ + +/* Mnemonics */ +static const char *hex_digit[] = + { "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "A", "B", "C", "D", "E", "F" }; + +static const char *reg_pair[] = + { "AB", "BC", "CA", "DC" }; + +static const char *rn_name[] = + { "R0", "R1", "R2", "R3", "R4", "?R5", "?R6", "?R7" }; + +static const char *field_sel[] = + { "P", "WP", "XS", "X", "S", "M", "B", "W", + "?[8]", "?[9]", "?[A]", "?[B]", "?[C]", "?[D]", "?[E]", "A" }; + +static const char *group_0_opc[] = + { "RTNSXM", "RTN", "RTNSC", "RTNCC", "SETHEX", "SETDEC", "RSTK=C", "C=RSTK", + "CLRST", "C=ST", "ST=C", "CSTEX", "P=P+1", "P=P-1", NULL, "RTI" }; + +static const char *group_13_opc[] = + { "D0=A", "D1=A", "AD0EX", "AD1EX", "D0=C", "D1=C", "CD0EX", "CD1EX", + "D0=AS", "D1=AS", "AD0XS", "AD1XS", "D0=CS", "D1=CS", "CD0XS", "CD1XS" }; + +static const char *group_14_opc[] = + { "DAT0=A\tA", "DAT1=A\tA", "A=DAT0\tA", "A=DAT1\tA", + "DAT0=C\tA", "DAT1=C\tA", "C=DAT0\tA", "C=DAT1\tA", + "DAT0=A\tB", "DAT1=A\tB", "A=DAT0\tB", "A=DAT1\tB", + "DAT0=C\tB", "DAT1=C\tB", "C=DAT0\tB", "C=DAT1\tB" }; + +static const char *group_15_opc[] = + { "DAT0=A", "DAT1=A", "A=DAT0", "A=DAT1", + "DAT0=C", "DAT1=C", "C=DAT0", "C=DAT1" }; + +static const char *group_80_opc[] = + { "OUT=CS", "OUT=C", "A=IN", "C=IN", "UNCNFG", "CONFIG", "C=ID", "SHUTDN", + "?", "C+P+1", "RESET", "BUSCC", "?", "?", "SREQ?", "?" }; + +static const char *group_81B_opc[] = + { "?", "?", "PC=A", "PC=C", "A=PC", "C=PC", "APCEX", "CPCEX", + "?", "?", "?", "?", "?", "?", "?", "?" }; + + +/* Get a nibble from main memory */ +#define GetNibble FetchNibble + + +/* Read two nibbles in two-complement form, starting from pc, */ +static Address Get2Nibbles2C(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4); + + return (v & 0x80) ? v - 0x100 : v; +} + + +/* Read three nibbles in two-complement form, starting from pc, */ +static Address Get3Nibbles2C(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4) | + ((Address)GetNibble(pc+2) << 8); + + return (v & 0x800) ? v - 0x1000 : v; +} + + +/* Read four nibbles in two-complement form, starting from pc */ +static Address Get4Nibbles2C(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4) | + ((Address)GetNibble(pc+2) << 8) | ((Address)GetNibble(pc+3) << 12); + + return (v & 0x8000) ? v - 0x10000 : v; +} + + +/* Read four nibbles in absolute form, starting from pc */ +static Address Get5NibblesAbs(Address pc) +{ + Address v = (Address)GetNibble(pc) | ((Address)GetNibble(pc+1) << 4) | + ((Address)GetNibble(pc+2) << 8) | ((Address)GetNibble(pc+3) << 12) | + ((Address)GetNibble(pc+4) << 16); + + return v; +} + + +/* Disassemble Hex constant, starting from 'start', for 'm' nibbles; + returns the address of the next instruction +*/ +static Address DisHexConstant(Address start, char *ob, int m) +{ + int i; + + for(i=0; i>1) | ((t & 0xC)>>2) + RP = t % 0x3 + + FS Field Selector + 0 P + 1 WP + 2 XS + 3 X + 4 S + 5 M + 6 B + 7 W + + OC Operation (Test) Code + 0 = + 1 # + 2 =0 + 3 #0 + 4 > + 5 < + 6 >= + 7 <= + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + f t OC RP + --- + A=B, B=A a 0 0 0 + B=C, C=B a 1 0 1 + C=A, A=C a 2 0 2 + D=C, C=D a 3 0 3 + --- + A#B, B#A a 4 1 0 + B#C, C#B a 5 1 1 + A#C, C#A a 6 1 2 + C#D, D#C a 7 1 3 + --- + A=0 a 8 2 0 + B=0 a 9 2 1 + C=0 a A 2 2 + D=0 a B 2 3 + --- + A#0 a C 3 0 + B#0 a D 3 1 + C#0 a E 3 2 + D#0 a F 3 3 + --- + A>B b 0 4 0 + B>C b 1 4 1 + C>A b 2 4 2 + D>C b 3 4 3 + --- + A=B b 8 6 0 + B>=C b 9 6 1 + C>=A b A 6 2 + D>=C b B 6 3 + --- + A<=B b C 7 0 + B<=C b D 7 1 + C<=A b E 7 2 + D<=C b F 7 3 + --- +*/ +static Address DisTest_9(Address pc, char *ob) +{ + Nibble f = GetNibble(pc++); + Nibble t = GetNibble(pc++); + + int fs = GetFS(f); + int tc = GetOC_2(f, t); + int rp = GetRP(t); + + /* Decode test code */ + switch(tc) + { + case 0: + sprintf(ob, "?%c=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "?%c#%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 2: + sprintf(ob, "?%c=0", reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "?%c#0", reg_pair[rp][0]); + break; + + case 4: + sprintf(ob, "?%c>%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 5: + sprintf(ob, "?%c<%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 6: + sprintf(ob, "?%c>=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 7: + sprintf(ob, "?%c<=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Test_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(fs, ob); + + /* Decode RTNYES/GOYES */ + return DisGOYES_RTNYES(pc, ob); +} + + +/* ..., Register Operation with Field Selector, opcode Afo, length 3 + + FS = f & 0x7 + OC = ((f & 0x8)>>1) | ((o & 0xC)>>2) + RP = o % 0x3 + + FS Field Selector + 0 P + 1 WP + 2 XS + 3 X + 4 S + 5 M + 6 B + 7 W + + OC Operation Code + 0 Add + 1 Double register + 2 Add, exchanging register pair + 3 Decrement register + 4 Clear register + 5 Copy register + 6 Copy register, exchanging register pair + 7 Exchange register pair contents + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + f o OC RP + --- + A=A+B a 0 0 0 + B=B+C a 1 0 1 + C=C+A a 2 0 2 + D=D+C a 3 0 3 + --- + A=A+A a 4 1 0 + B=B+B a 5 1 1 + C=C+C a 6 1 2 + D=D+D a 7 1 3 + --- + B=B+A a 8 2 0 + C=C+B a 9 2 1 + A=A+C a A 2 2 + C=C+D a B 2 3 + --- + A=A-1 a C 3 0 + B=B-1 a D 3 1 + C=C-1 a E 3 2 + D=D-1 a F 3 3 + --- + A=0 b 0 4 0 + B=0 b 1 4 1 + C=0 b 2 4 2 + D=0 b 3 4 3 + --- + A=B b 4 5 0 + B=C b 5 5 1 + C=A b 6 5 2 + D=C c 7 5 3 + --- + B=A b 8 6 0 + C=B b 9 6 1 + A=C b A 6 2 + C=D b B 6 3 + --- + ABEX b C 7 0 + BCEX b D 7 1 + ACEX b E 7 2 + CDEX b F 7 3 + --- + +*/ +static Address DisRegOp_A(Address pc, char *ob) +{ + Nibble f = GetNibble(pc++); + Nibble o = GetNibble(pc++); + + int fs = GetFS(f); + int oc = GetOC_2(f, o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%c=%c+%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "%c=%c+%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 2: + sprintf(ob, "%c=%c+%c", + reg_pair[rp][1], reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "%c=%c-1", reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 4: + sprintf(ob, "%c=0", reg_pair[rp][0]); + break; + + case 5: + sprintf(ob, "%c=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 6: + sprintf(ob, "%c=%c", reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 7: + sprintf(ob, "%c%cEX", reg_pair[rp][0], reg_pair[rp][1]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(fs, ob); + + return pc; +} + + +/* ..., Register Operation with Field Selector, opcode Bfo, length 3 + + FS = f & 0x7 + OC = ((f & 0x8)>>1) | ((o & 0xC)>>2) + RP = o % 0x3 + + FS FIeld Selector + 0 P + 1 WP + 2 XS + 3 X + 4 S + 5 M + 6 B + 7 W + + OC Operation Code + 0 Subtract + 1 Increment register + 2 Subtract, exchanging register pair + 3 Subtract, exchanging operands only + 4 Shift register left one nibble + 5 Shift register right one nibble, set sticky bit (SB) if #0 + 6 Two's / Ten's complement register + 7 One's complement register + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + f o OC RP + --- + A=A-B a 0 0 0 + B=B-C a 1 0 1 + C=C-A a 2 0 2 + D=D-C a 3 0 3 + --- + A=A+1 a 4 1 0 + B=B+1 a 5 1 1 + C=C+1 a 6 1 2 + D=D+1 a 7 1 3 + --- + B=B-A a 8 2 0 + C=C-B a 9 2 1 + A=A-C a A 2 2 + C=C-D a B 2 3 + --- + A=B-A a C 3 0 + B=C-B a D 3 1 + C=A-C a E 3 2 + D=C-D a F 3 3 + --- + ASL b 0 4 0 + BSL b 1 4 1 + CSL b 2 4 2 + DSL b 3 4 3 + --- + ASR b 4 5 0 + BSR b 5 5 1 + CSR b 6 5 2 + DSR b 7 5 3 + --- + A=-A b 8 6 0 + B=-B b 9 6 1 + C=-C b A 6 2 + D=-D b B 6 3 + --- + A=-A-1 b C 7 0 + B=-B-1 b D 7 1 + C=-C-1 b E 7 2 + D=-D-1 b F 7 3 + --- + +*/ +static Address DisRegOp_B(Address pc, char *ob) +{ + Nibble f = GetNibble(pc++); + Nibble o = GetNibble(pc++); + + int fs = GetFS(f); + int oc = GetOC_2(f, o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%c=%c-%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "%c=%c+1", reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 2: + sprintf(ob, "%c=%c-%c", + reg_pair[rp][1], reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "%c=%c-%c", + reg_pair[rp][0], reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 4: + sprintf(ob, "%cSL", reg_pair[rp][0]); + break; + + case 5: + sprintf(ob, "%cSR", reg_pair[rp][0]); + break; + + case 6: + sprintf(ob, "%c=-%c", reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 7: + sprintf(ob, "%c=-%c-1", reg_pair[rp][0], reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(fs, ob); + + return pc; +} + + +/* ..., Register Operation on A Fields, opcode Co, length 2 + + FS = implicit, always A + OC = ((o & 0xC)>>2) + RP = o % 0x3 + + OC Operation Code + 0 Add + 1 Double register + 2 Add, exchanging register pair + 3 Decrement register + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + o OC RP + --- + A=A+B 0 0 0 + B=B+C 1 0 1 + C=C+A 2 0 2 + D=D+C 3 0 3 + --- + A=A+A 4 1 0 + B=B+B 5 1 1 + C=C+C 6 1 2 + D=D+D 7 1 3 + --- + B=B+A 8 2 0 + C=C+B 9 2 1 + A=A+C A 2 2 + C=C+D B 2 3 + --- + A=A-1 C 3 0 + B=B-1 D 3 1 + C=C-1 E 3 2 + D=D-1 F 3 3 + --- +*/ +static Address DisRegOp_C(Address pc, char *ob) +{ + Nibble o = GetNibble(pc++); + + int oc = GetOC_1(o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%c=%c+%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "%c=%c+%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 2: + sprintf(ob, "%c=%c+%c", + reg_pair[rp][1], reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "%c=%c-1", reg_pair[rp][0], reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(FS_A, ob); + + return pc; +} + + +/* ..., Register Operation on A Fields, opcode Do, length 2 + + FS = implicit, always A + OC = ((o & 0xC)>>2) + RP = o % 0x3 + + OC Operation Code + 0 Clear register + 1 Copy register + 2 Copy register, exchanging register pair + 3 Exchange register pair contents + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + o OC RP + --- + A=0 0 0 0 + B=0 1 0 1 + C=0 2 0 2 + D=0 3 0 3 + --- + A=B 4 1 0 + B=C 5 1 1 + C=A 6 1 2 + D=C 7 1 3 + --- + B=A 8 2 0 + C=B 9 2 1 + A=C A 2 2 + C=D B 2 3 + --- + ABEX C 3 0 + BCEX D 3 1 + ACEX E 3 2 + CDEX F 3 3 + +*/ +static Address DisRegOp_D(Address pc, char *ob) +{ + Nibble o = GetNibble(pc++); + + int oc = GetOC_1(o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%c=0", reg_pair[rp][0]); + break; + + case 1: + sprintf(ob, "%c=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 2: + sprintf(ob, "%c=%c", reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "%c%cEX", reg_pair[rp][0], reg_pair[rp][1]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(FS_A, ob); + + return pc; +} + + +/* ..., Register Operation on A Fields, opcode Eo, length 2 + + FS = implicit, always A + OC = ((o & 0xC)>>2) + RP = o % 0x3 + + OC Operation Code + 0 Subtract + 1 Increment register + 2 Subtract, exchanging register pair + 3 Subtract, exchanging operands only + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + o OC RP + --- + A=A-B 0 0 0 + B=B-C 1 0 1 + C=C-A 2 0 2 + D=D-C 3 0 3 + --- + A=A+1 4 1 0 + B=B+1 5 1 1 + C=C+1 6 1 2 + D=D+1 7 1 3 + --- + B=B-A 8 2 0 + C=C-B 9 2 1 + A=A-C A 2 2 + C=C-D B 2 3 + --- + A=B-A C 3 0 + B=C-B D 3 1 + C=A-C E 3 2 + D=C-D F 3 3 + --- + +*/ +static Address DisRegOp_E(Address pc, char *ob) +{ + Nibble o = GetNibble(pc++); + + int oc = GetOC_1(o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%c=%c-%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "%c=%c+1", reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 2: + sprintf(ob, "%c=%c-%c", + reg_pair[rp][1], reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "%c=%c-%c", + reg_pair[rp][0], reg_pair[rp][1], reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(FS_A, ob); + + return pc; +} + + +/* ..., Register Operation on A Fields, opcode Fo, length 2 + + FS = implicit, always A + OC = ((o & 0xC)>>2) + RP = o % 0x3 + + OC Operation Code + 0 Shift register left one nibble + 1 Shift register right one nibble, set sticky bit (SB) if #0 + 2 Two's / Ten's complement register + 3 One's complement register + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + o OC RP + --- + ASL 0 0 0 + BSL 1 0 1 + CSL 2 0 2 + DSL 3 0 3 + --- + ASR 4 1 0 + BSR 5 1 1 + CSR 6 1 2 + DSR 7 1 3 + --- + A=-A 8 2 0 + B=-B 9 2 1 + C=-C A 2 2 + D=-D B 2 3 + --- + A=-A-1 C 3 0 + B=-B-1 D 3 1 + C=-C-1 E 3 2 + D=-D-1 F 3 3 + --- + +*/ +static Address DisRegOp_F(Address pc, char *ob) +{ + Nibble o = GetNibble(pc++); + + int oc = GetOC_1(o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%cSL", reg_pair[rp][0]); + break; + + case 1: + sprintf(ob, "%cSR", reg_pair[rp][0]); + break; + + case 2: + sprintf(ob, "%c=-%c", reg_pair[rp][0], reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "%c=-%c-1", reg_pair[rp][0], reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(FS_A, ob); + + return pc; +} + + +/* .&., .!., AND/OR Operations, opcode 0Efo, length 4 + + FS = f + OC = ((o & 0xC)>>2) + RP = o % 0x3 + + FS FIeld Selector + 0 P + 1 WP + 2 XS + 3 X + 4 S + 5 M + 6 B + 7 W + 8 A + + OC Operation Code + 0 And + 1 And, exchanging register pair + 2 Or + 3 Or, exchanging register pair + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + f o OC RP + --- + A=A&B a 0 0 0 + B=B&C a 1 0 1 + C=C&A a 2 0 2 + D=D&C a 3 0 3 + --- + B=B&A a 4 1 0 + C=C&B a 5 1 1 + A=A&C a 6 1 2 + C=C&D a 7 1 3 + --- + A=A!B a 8 2 0 + B=B!C a 9 2 1 + C=C!A a A 2 2 + D=D!C a B 2 3 + --- + B=B!A a C 3 0 + C=C!B a D 3 1 + A=A!C a E 3 2 + C=C!D a F 3 3 + --- + +*/ +static Address DisAND_OR(Address pc, char *ob) +{ + Nibble f = GetNibble(pc++); + Nibble o = GetNibble(pc++); + + int oc = GetOC_1(o); + int rp = GetRP(o); + + /* Decode operation code */ + switch(oc) + { + case 0: + sprintf(ob, "%c=%c&%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "%c=%c&%c", + reg_pair[rp][1], reg_pair[rp][1], reg_pair[rp][0]); + break; + + case 2: + sprintf(ob, "%c=%c!%c", + reg_pair[rp][0], reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 3: + sprintf(ob, "%c=%c!%c", + reg_pair[rp][1], reg_pair[rp][1], reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(f, ob); + + return pc; +} + + +/* Instruction Group_0 + Prefix 0E introduces AND/OR opcodes +*/ +static Address DisGroup_0(Address pc, char *ob) +{ + Nibble n = GetNibble(pc++); + + switch(n) + { + case 0: + /* RTNSXM */ + case 1: + /* RTN */ + case 2: + /* RTNSC */ + case 3: + /* RTNCC */ + case 4: + /* SETHEX */ + case 5: + /* SETDEC */ + case 6: + /* RSTK=C */ + case 7: + /* C=RSTK */ + case 8: + /* CLRST */ + case 9: + /* C=ST */ + case 0xA: + /* ST=C */ + case 0xB: + /* CSTEX */ + case 0xC: + /* P=P+1 */ + case 0xD: + /* P=P-1 */ + case 0xF: + /* RTI */ + strcpy(ob, group_0_opc[(int)n]); + break; + + case 0xE: + /* AND_OR */ + pc = DisAND_OR(pc, ob); + break; + + default: + /* Unknown opcode */ + strcpy(ob, "?"); + + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + return pc; +} + + +/* Instruction Group_1 + + Opcode table for: + 10r Rn = A/C (Copy A/C into R0..R4) + 11r A/C = Rn (Copy R0..R4 into A/C) + 12r ARnEX, CRnEX (Exchange A/C and R0..R4) + + RN = r & 0x7 + AC = r & 0x8 + Opcode RN AC + --- + R0=A 100 0 0 + R1=A 101 1 0 + R2=A 102 2 0 + R3=A 103 3 0 + R4=A 104 4 0 + --- + R0=C 108 0 8 + R1=C 109 1 8 + R2=C 10A 2 8 + R3=C 10B 3 8 + R4=C 10C 4 8 + --- + A=R0 110 0 0 + A=R1 111 1 0 + A=R2 112 2 0 + A=R3 113 3 0 + A=R4 114 4 0 + --- + C=R0 118 0 8 + C=R1 119 1 8 + C=R2 11A 2 8 + C=R3 11B 3 8 + C=R4 11C 4 8 + --- + AR0EX 120 0 0 + AR1EX 121 1 0 + AR2EX 122 2 0 + AR3EX 123 3 0 + AR4EX 124 4 0 + --- + CR0EX 128 0 8 + CR1EX 129 1 8 + CR2EX 12A 2 8 + CR3EX 12B 3 8 + CR4EX 12C 4 8 + + Opcode table for: + 13r + Opcode + --- + D0=A 130 + D1=A 131 + AD0EX 132 + AD1EX 133 + D0=C 134 + D1=C 135 + CD0EX 136 + CD1EX 137 + D0=AS 138 + D1=AS 139 + AD0XS 13A + AD1XS 13B + D0=CS 13C + D1=CS 13D + CD0XS 13E + CD1XS 13F + + + Opcode table for: + 14r + Opcode + --- + DAT0=A A 140 + DAT1=A A 141 + A=DAT0 A 142 + A=DAT1 A 143 + DAT0=C A 144 + DAT1=C A 145 + C=DAT0 A 146 + C=DAT1 A 147 + DAT0=A B 148 + DAT1=A B 149 + A=DAT0 B 14A + A=DAT1 B 14B + DAT0=C B 14C + DAT1=C B 14D + C=DAT0 B 14E + C=DAT1 B 14F + + + Opcode table for: + 15of + + OC = o & 0x7 + IS = o & 0x8 + + o f OC IS + --- + DAT0=A fs 0 a 0 0 + DAT1=A fs 1 a 1 0 + A=DAT0 fs 2 a 2 0 + A=DAT1 fs 3 a 3 0 + DAT0=C fs 4 a 4 0 + DAT1=C fs 5 a 5 0 + C=DAT0 fs 6 a 6 0 + C=DAT1 fs 7 a 7 0 + DAT0=A d 8 d-1 0 8 + DAT1=A d 9 d-1 1 8 + A=DAT0 d A d-1 2 8 + A=DAT1 d B d-1 3 8 + DAT0=C d C d-1 4 8 + DAT1=C d D d-1 5 8 + C=DAT0 d E d-1 6 8 + C=DAT1 d F d-1 7 8 + + + Opcode table for: + 16m, D0=D0+m+1 + 17m, D1=D1+m+1 + 18m, D0=D0-(m+1) + 1Cm, D1=D1-(m+1) + + Opcode table for: + 19nn D0=(2) nn + 1Annnn D0=(4) nnnn + 1Bnnnnn D0=(5) nnnnn + 1Dnn D1=(2) nn + 1Ennnn D1=(4) nnnn + 1Fnnnnn D1=(5) nnnnn + + +*/ +static Address DisGroup_1(Address pc, char *ob) +{ + Nibble n = GetNibble(pc++); + Nibble f; + int rn, ac; + int oc, is; + + switch(n) + { + case 0: + /* Rn=A/C */ + n = GetNibble(pc++); + rn = GetRn(n); + ac = GetAC(n); + + sprintf(ob, "%s=%s", rn_name[rn], (ac ? "C" : "A")); + break; + + case 1: + /* A/C=Rn */ + n = GetNibble(pc++); + rn = GetRn(n); + ac = GetAC(n); + + sprintf(ob, "%s=%s", (ac ? "C" : "A"), rn_name[rn]); + break; + + case 2: + /* ARnEX, CRnEX */ + n = GetNibble(pc++); + rn = GetRn(n); + ac = GetAC(n); + + sprintf(ob, "%s%sEX", (ac ? "C" : "A"), rn_name[rn]); + break; + + case 3: + /* Copy/Exchange A/C and D0/D1 */ + n = GetNibble(pc++); + strcpy(ob, group_13_opc[(int)n]); + break; + + case 4: + /* Load/Store A/C to @D0/@D1, Field selector A or B */ + n = GetNibble(pc++); + strcpy(ob, group_14_opc[(int)n]); + break; + + case 5: + /* Load/Store A/C to @D0/@D1, Other Field Selectors */ + n = GetNibble(pc++); + f = GetNibble(pc++); + oc = GetOC_3b(n); + is = GetImmFS(n); + + /* Decode operation code */ + strcpy(ob, group_15_opc[oc]); + + if(is) + /* Immediate field selector */ + DisIMM_FIELD_SEL(f, ob); + + else + /* Regular field selector */ + DisFIELD_SEL(f, ob); + + break; + + case 6: + /* D0=D0+n+1 */ + n = GetNibble(pc++); + sprintf(ob, "D0=D0+%d", n+1); + break; + + case 7: + /* D1=D1+n+1 */ + n = GetNibble(pc++); + sprintf(ob, "D1=D1+%d", n+1); + break; + + case 8: + /* D0=D0-(n+1) */ + n = GetNibble(pc++); + sprintf(ob, "D0=D0-%d", n+1); + break; + + case 9: + /* D0=(2) nn */ + strcpy(ob, "D0=(2)\t"); + pc = DisHexConstant(pc, ob, 2); + break; + + case 0xA: + /* D0=(4) nn */ + strcpy(ob, "D0=(4)\t"); + pc = DisHexConstant(pc, ob, 4); + break; + + case 0xB: + /* D0=(5) nn */ + strcpy(ob, "D0=(5)\t"); + pc = DisHexConstant(pc, ob, 5); + break; + + case 0xC: + /* D1=D1-(n+1) */ + n = GetNibble(pc++); + sprintf(ob, "D1=D1-%d", n+1); + break; + + case 0xD: + /* D1=(2) nn */ + strcpy(ob, "D1=(2)\t"); + pc = DisHexConstant(pc, ob, 2); + break; + + case 0xE: + /* D1=(4) nn */ + strcpy(ob, "D1=(4)\t"); + pc = DisHexConstant(pc, ob, 4); + break; + + case 0xF: + /* D1=(5) nn */ + strcpy(ob, "D1=(5)\t"); + pc = DisHexConstant(pc, ob, 5); + break; + + default: + /* Unknown opcode */ + strcpy(ob, "?"); + + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + return pc; +} + + +/* Instruction Group_808 +*/ +static Address DisGroup_808(Address pc, char *ob) +{ + Nibble n = GetNibble(pc++); + Nibble m; + + switch(n) + { + case 0: + /* INTON */ + strcpy(ob, "INTON"); + break; + + case 1: + /* RSI */ + strcpy(ob, "RSI"); + pc++; + break; + + case 2: + /* LA(m) n..n */ + m = GetNibble(pc++)+1; + sprintf(ob, "LA(%d)\t", m); + pc = DisHexConstant(pc, ob, m); + break; + + case 3: + /* BUSCB */ + strcpy(ob, "BUSCB"); + break; + + case 4: + /* ABIT=0 d */ + m = GetNibble(pc++); + sprintf(ob, "ABIT=0 %d", m); + break; + + case 5: + /* ABIT=1 d */ + m = GetNibble(pc++); + sprintf(ob, "ABIT=1 %d", m); + break; + + case 6: + /* ?ABIT=0 d */ + m = GetNibble(pc++); + sprintf(ob, "?ABIT=0 %d", m); + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 7: + /* ?ABIT=1 d */ + m = GetNibble(pc++); + sprintf(ob, "?ABIT=1 %d", m); + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 8: + /* CBIT=0 d */ + m = GetNibble(pc++); + sprintf(ob, "CBIT=0 %d", m); + break; + + case 9: + /* CBIT=1 d */ + m = GetNibble(pc++); + sprintf(ob, "CBIT=1 %d", m); + break; + + case 0xA: + /* ?CBIT=0 d */ + m = GetNibble(pc++); + sprintf(ob, "?CBIT=0 %d", m); + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 0xB: + /* ?CBIT=1 d */ + m = GetNibble(pc++); + sprintf(ob, "?CBIT=1 %d", m); + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 0xC: + /* PC=(A) */ + strcpy(ob, "PC=(A)"); + break; + + case 0xD: + /* BUSCD */ + strcpy(ob, "BUSCD"); + break; + + case 0xE: + /* PC=(C) */ + strcpy(ob, "PC=(C)"); + break; + + case 0xF: + /* INTOFF */ + strcpy(ob, "INTOFF"); + break; + + default: + /* Unknown opcode */ + strcpy(ob, "?"); + + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + return pc; +} + +/* Instruction Group_80 +*/ +static Address DisGroup_80(Address pc, char *ob) +{ + Nibble n = GetNibble(pc++); + + switch(n) + { + case 0: + /* OUT=CS */ + case 1: + /* OUT=C */ + case 2: + /* A=IN */ + case 3: + /* C=IN */ + case 4: + /* UNCNFG */ + case 5: + /* CONFIG */ + case 6: + /* C=ID */ + case 7: + /* SHUTDN */ + case 9: + /* C+P+1 */ + case 0xA: + /* RESET */ + case 0xB: + /* BUSCC */ + case 0xE: + /* SREQ? */ + strcpy(ob, group_80_opc[(int)n]); + break; + + case 8: + /* Group 808 */ + pc = DisGroup_808(pc, ob); + break; + + case 0xC: + /* C=P n */ + n = GetNibble(pc++); + sprintf(ob, "C=P\t%d", n); + break; + + case 0xD: + /* P=C n */ + n = GetNibble(pc++); + sprintf(ob, "P=C\t%d", n); + break; + + case 0xF: + /* CPEX */ + n = GetNibble(pc++); + sprintf(ob, "CPEX\t%d", n); + break; + + default: + /* Unknown opcode */ + strcpy(ob, "?"); + + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + return pc; +} + + +/* Special functions Group_81 + + Opcode Table for: + rp=0, opcode 818f0m, r=r+-CON rfs, d + f= field selector + m= d-1 + + Opcode + --- + A=A+CON rfs,d 818f0m + A=A-CON rfs,d 818f8m + B=B+CON rfs,d 818f1m + B=B-CON rfs,d 818f9m + C=C+CON rfs,d 818f2m + C=C-CON rfs,d 818fAm + D=D+CON rfs,d 818f3m + D=D-CON rfs,d 818fBm + --- + + Opcode Table for: + rp=1, opcode 819.., rSRB.F fs + --- + ASRB.F fs 819f0 + BSRB.F fs 819f1 + CSRB.F fs 819f2 + DSRB.F fs 819f3 + --- + + rp=2, opcode 81A.. + --- + R0=A.F fs 81Af00 + R1=A.F fs 81Af01 + R2=A.F fs 81Af02 + R3=A.F fs 81Af03 + R4=A.F fs 81Af04 + --- + R0=C.F fs 81Af08 + R1=C.F fs 81Af09 + R2=C.F fs 81Af0A + R3=C.F fs 81Af0B + R4=C.F fs 81Af0C + --- + A=R0.F fs 81Af10 + A=R1.F fs 81Af11 + A=R2.F fs 81Af12 + A=R3.F fs 81Af13 + A=R4.F fs 81Af14 + --- + C=R0.F fs 81Af18 + C=R1.F fs 81Af19 + C=R2.F fs 81Af1A + C=R3.F fs 81Af1B + C=R4.F fs 81Af1C + --- + AR0EX.F fs 81Af20 + AR1EX.F fs 81Af21 + AR2EX.F fs 81Af22 + AR3EX.F fs 81Af23 + AR4EX.F fs 81Af24 + --- + CR0EX.F fs 81Af28 + CR1EX.F fs 81Af29 + CR2EX.F fs 81Af2A + CR3EX.F fs 81Af2B + CR4EX.F fs 81Af2C + + + Opcode Table for: + rp=3, opcode 81Bn + Direct execution + +*/ +static Address DisSpecialGroup_81(Address pc, char *ob, int rp) +{ + Nibble n, f, m; + int rn, ac; + + switch(rp) + { + case 0: + /* r=r+-CON fs, d */ + f = GetNibble(pc++); + n = GetNibble(pc++); + m = GetNibble(pc++); + rp = GetRP(n); + + sprintf(ob, "%c=%c%cCON", + reg_pair[rp][0], reg_pair[rp][0], (GetAS(n) ? '-' : '+' )); + + /* Decode field selector */ + DisFIELD_SEL(f, ob); + + /* Decode constant */ + ob += strlen(ob); + sprintf(ob, ", %d", m+1); + break; + + case 1: + /* rSRB.f fs */ + f = GetNibble(pc++); + n = GetNibble(pc++); + rp = GetRP(n); + + sprintf(ob, "%cSRB.F", reg_pair[rp][0]); + + /* Decode field selector */ + DisFIELD_SEL(f, ob); + break; + + case 2: + /* Rn=r.F fs, r=R0.F fs, rRnEX.F fs */ + f = GetNibble(pc++); + n = GetNibble(pc++); + m = GetNibble(pc++); + rn = GetRn(m); + ac = GetAC(m); + + switch(n) + { + case 0: + /* Rn=r.F fs */ + sprintf(ob, "%s=%s.F", rn_name[rn], (ac ? "C" : "A")); + DisFIELD_SEL(f, ob); + break; + + case 1: + /* r=R0.F fs */ + sprintf(ob, "%s=%s.F", (ac ? "C" : "A"), rn_name[rn]); + DisFIELD_SEL(f, ob); + break; + + case 2: + /* rRnEX.F fs */ + sprintf(ob, "%s%sEX.F", (ac ? "C" : "A"), rn_name[rn]); + DisFIELD_SEL(f, ob); + break; + + default: + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + break; + + case 3: + /* Group 81B */ + n = GetNibble(pc++); + strcpy(ob, group_81B_opc[(int)n]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Register_Pair" ChfEnd; + ChfSignal(); + break; + } + + return pc; +} + + +/* ?..., GOYES/RTNYES, Test on A Fields, opcode 8Atyy, length 5 + + FS = implicit, always A + OC = ((t & 0xC)>>2) + RP = t % 0x3 + + OC Operation (Test) Code + 0 = + 1 # + 2 =0 + 3 #0 + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + t OC RP + --- + A=B, B=A 0 0 0 + B=C, C=B 1 0 1 + C=A, A=C 2 0 2 + D=C, C=D 3 0 3 + --- + A#B, B#A 4 1 0 + B#C, C#B 5 1 1 + A#C, C#A 6 1 2 + C#D, D#C 7 1 3 + --- + A=0 8 2 0 + B=0 9 2 1 + C=0 A 2 2 + D=0 B 2 3 + --- + A#0 C 3 0 + B#0 D 3 1 + C#0 E 3 2 + D#0 F 3 3 + --- + +*/ +static Address DisTest_8A(Address pc, char *ob) +{ + Nibble t = GetNibble(pc++); + + int tc = GetOC_1(t); + int rp = GetRP(t); + + /* Decode test code */ + switch(tc) + { + case 0: + sprintf(ob, "?%c=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "?%c#%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 2: + sprintf(ob, "?%c=0", reg_pair[rp][0]); + break; + + case 3: + sprintf(ob, "?%c#0", reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Test_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(FS_A, ob); + + /* Decode RTNYES/GOYES */ + return DisGOYES_RTNYES(pc, ob); +} + + +/* ?..., GOYES/RTNYES, Test on A Fields, opcode 8Btyy, length 5 + + FS = implicit, always A + OC = ((t & 0xC)>>2) + RP = t % 0x3 + + OC Operation (Test) Code + 0 > + 1 < + 2 >= + 3 <= + + RP Register Pair + 0 A,B + 1 B,C + 2 C,A + 3 D,C + + Opcode table + t OC RP + --- + A>B 0 0 0 + B>C 1 0 1 + C>A 2 0 2 + D>C 3 0 3 + --- + A=B 8 2 0 + B>=C 9 2 1 + C>=A A 2 2 + D>=C B 2 3 + --- + A<=B C 3 0 + B<=C D 3 1 + C<=A E 3 2 + D<=C F 3 3 + --- + +*/ +static Address DisTest_8B(Address pc, char *ob) +{ + Nibble t = GetNibble(pc++); + + int tc = GetOC_1(t); + int rp = GetRP(t); + + /* Decode test code */ + switch(tc) + { + case 0: + sprintf(ob, "?%c>%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 1: + sprintf(ob, "?%c<%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 2: + sprintf(ob, "?%c>=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + case 3: + sprintf(ob, "?%c<=%c", reg_pair[rp][0], reg_pair[rp][1]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Test_Code" ChfEnd; + ChfSignal(); + break; + } + + /* Decode field selector */ + DisFIELD_SEL(FS_A, ob); + + /* Decode RTNYES/GOYES */ + return DisGOYES_RTNYES(pc, ob); +} + + +/* Instruction Group_8 + + Opcode table for: + 81s + + OC = (s & 0xC) >> 2 + RP = (s & 0x3) + + Opcode OC RP + --- + ASLC 810 0 0 + BSLC 811 0 1 + CSLC 812 0 2 + DSLC 813 0 3 + --- + ASRC 814 1 0 + BSRC 815 1 1 + CSRC 816 1 2 + DSRC 817 1 3 + --- + 818 2 0, Special + 819 2 1, Special + 81A 2 2, Special + 81B 2 3, Special + --- + ASRB 81C 3 0 + BSRB 81D 3 1 + CSRB 81E 3 2 + DSRB 81F 3 3 + --- + + Opcode table for: + 82n, CLRHSN n, Clear one or more HST bits, n=bit mask + 83nyy, ?HS=0 n, Check HST bits, n=bit mask + 84n ST=0 n, Clear bit n of ST register + 85n ST=1 n, Set bit n of ST register + 86nyy, ?ST=0 n, Check ST bits, n=bit number + 87nyy, ?ST=1 n, Check ST bits, n=bit number + 88nyy, ?P#n, Check P#n, jump/return/set carry if true + 89nyy, ?P=n, Check P=n, jump/return/set carry if true + 8A..., Tests + 8B..., Tests + 8Cnnnn, GOLONG + 8Dnnnnn, GOVLNG + 8Ennnn, GOSUBL + 8Fnnnnn, GOSBVL +*/ +static Address DisGroup_8(Address pc, char *ob) +{ + Nibble n = GetNibble(pc++); + Address addr; + int oc, rp; + + switch(n) + { + case 0: + pc = DisGroup_80(pc, ob); + break; + + case 1: + /* rSLC, rSRC, rSRB, Special Group_81 */ + n = GetNibble(pc++); + oc = GetOC_1(n); + rp = GetRP(n); + + switch(oc) + { + case 0: + /* rSLC */ + sprintf(ob, "%cSLC", reg_pair[rp][0]); + break; + + case 1: + /* rSRC */ + sprintf(ob, "%cSRC", reg_pair[rp][0]); + break; + + case 2: + /* Special Group_81 */ + pc = DisSpecialGroup_81(pc, ob, rp); + break; + + case 3: + /* rSRB */ + sprintf(ob, "%cSRB", reg_pair[rp][0]); + break; + + default: + ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd; + ChfSignal(); + break; + } + break; + + case 2: + /* CLRHSn */ + n = GetNibble(pc++); + + switch(n) + { + case 1: + strcpy(ob, "XM=0"); + break; + + case 2: + strcpy(ob, "SB=0"); + break; + + case 4: + strcpy(ob, "SR=0"); + break; + + case 8: + strcpy(ob, "MP=0"); + break; + + case 0xF: + strcpy(ob, "CLRHST"); + break; + + default: + sprintf(ob, "CLRHSN\t%X", n); + break; + } + break; + + case 3: + /* ?HS=0 */ + n = GetNibble(pc++); + + switch(n) + { + case 1: + strcpy(ob, "?XM=0"); + break; + + case 2: + strcpy(ob, "?SB=0"); + break; + + case 4: + strcpy(ob, "?SR=0"); + break; + + case 8: + strcpy(ob, "?MP=0"); + break; + + default: + sprintf(ob, "?HS=0\t%X", n); + break; + } + + /* Decode RTNYES/GOYES */ + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 4: + /* ST=0 n */ + n = GetNibble(pc++); + + sprintf(ob, "ST=0\t%d", n); + break; + + case 5: + /* ST=1 n */ + n = GetNibble(pc++); + + sprintf(ob, "ST=1\t%d", n); + break; + + case 6: + /* ?ST=0 n */ + n = GetNibble(pc++); + + /* Decode bit number */ + sprintf(ob, "?ST=0\t%d", n); + + /* Decode RTNYES/GOYES */ + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 7: + /* ?ST=1 n */ + n = GetNibble(pc++); + + /* Decode bit number */ + sprintf(ob, "?ST=1\t%d", n); + + /* Decode RTNYES/GOYES */ + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 8: + /* ?P#n */ + n = GetNibble(pc++); + + /* Decode bit number */ + sprintf(ob, "?P#%d", n); + + /* Decode RTNYES/GOYES */ + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 9: + /* ?P=n */ + n = GetNibble(pc++); + + /* Decode bit number */ + sprintf(ob, "?P=%d", n); + + /* Decode RTNYES/GOYES */ + pc = DisGOYES_RTNYES(pc, ob); + break; + + case 0xA: + /* Test */ + pc = DisTest_8A(pc, ob); + break; + + case 0xB: + /* Test */ + pc = DisTest_8B(pc, ob); + break; + + case 0xC: + /* GOLONG */ + addr = Get4Nibbles2C(pc); + sprintf(ob, "GOLONG\tA_%05X\t* Offset [%d]d", pc+addr, addr); + pc += 4; + break; + + case 0xD: + /* GOVLNG */ + addr = Get5NibblesAbs(pc); + sprintf(ob, "GOVLNG\tA_%05X", addr); + pc += 5; + break; + + case 0xE: + /* GOSUBL */ + addr = Get4Nibbles2C(pc); + pc += 4; + + sprintf(ob, "GOSUBL\tA_%05X\t* Offset [%d]d", pc+addr, addr); + break; + break; + + case 0xF: + /* GOSBVL */ + addr = Get5NibblesAbs(pc); + sprintf(ob, "GOSBVL\tA_%05X", addr); + pc += 5; + break; + + default: + /* Unknown opcode */ + strcpy(ob, "?"); + + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + return pc; +} + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : Disassemble +.kind : C function +.creation : 28-Jan-1998 +.description : + This function disassembles a Saturn instruction starting from 'pc' and + puts the result into the output buffer 'ob'. + +.call : + new_pc = Disassemble(pc, ob) +.input : + Address pc, address of the instruction to disassemble +.output : + char ob[DISASSEMBLE_OB_SIZE], text output buffer + Address new_pc, address of the next instruction +.status_codes : + CPU_E_BAD_OPCODE + CPU_F_INTERR +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +Address Disassemble(Address pc, char ob[DISASSEMBLE_OB_SIZE]) +{ + Nibble n; + + /* Disassemble current program counter */ + sprintf(ob,"A_%05X\t", pc); + ob += strlen(ob); + + /* Get first instruction nibble */ + n = GetNibble(pc++); + + switch(n) + { + case 0: + /* Group_0 */ + pc = DisGroup_0(pc, ob); break; + + case 1: + /* Group_1 */ + pc = DisGroup_1(pc, ob); break; + + case 2: + /* P=n */ + pc = DisPEqn(pc, ob); break; + + case 3: + /* LC(m) n...n */ + pc = DisLC(pc, ob); break; + + case 4: + /* RTNC/GOC */ + pc = DisRTNC_GOC(pc, ob); break; + + case 5: + /* RTNNC/GONC */ + pc = DisRTNNC_GONC(pc, ob); break; + + case 6: + /* GOTO */ + pc = DisGOTO(pc, ob); break; + + case 7: + /* GOSUB */ + pc = DisGOSUB(pc, ob); break; + + case 8: + /* Group_8 */ + pc = DisGroup_8(pc, ob); break; + + case 9: + /* Test */ + pc = DisTest_9(pc, ob); break; + + case 0xA: + /* Register Operation, group A */ + pc = DisRegOp_A(pc, ob); break; + + case 0xB: + /* Register Operation, group B */ + pc = DisRegOp_B(pc, ob); break; + + case 0xC: + /* Register Operation, group C */ + pc = DisRegOp_C(pc, ob); break; + + case 0xD: + /* Register Operation, group D */ + pc = DisRegOp_D(pc, ob); break; + + case 0xE: + /* Register Operation, group E */ + pc = DisRegOp_E(pc, ob); break; + + case 0xF: + /* Register Operation, group F */ + pc = DisRegOp_F(pc, ob); break; + + default: + /* Unknown opcode */ + strcpy(ob, "?"); + + ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, pc, n ChfEnd; + ChfSignal(); + break; + } + + return pc & ADDRESS_MASK; +} diff --git a/disk_io.c b/disk_io.c new file mode 100644 index 0000000..b071746 --- /dev/null +++ b/disk_io.c @@ -0,0 +1,322 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: disk_io.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: disk_io.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 23-Jan-1998 +.keywords : * +.description : + This module implements the disk I/O functions used by the emulator to + save and restore the machine status to/from disk files. + +.include : config.h, machdep.h, cpu.h, disk_io.h + +.notes : + $Log: disk_io.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:36 cibrario + Added/Replaced GPL header + + Revision 1.1 1998/02/17 11:54:38 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: disk_io.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "disk_io.h" +#include "debug.h" + +#define CHF_MODULE_ID DISK_IO_CHF_MODULE_ID +#include + + +/* .+ + +.title : ReadNibblesFromFile +.kind : C function +.creation : 11-Feb-1998 +.description : + This function reads 'size' nibbles from the disk file named 'name', + and stores them into main memory starting from 'dest'. It returns to the + caller a status code. + +.call : + st = ReadNibbledFromFile(name, size, dest); +.input : + const char *name, file name + int size, size of the file (nibbles, NOT bytes) +.output : + Nibble *dest, pointer to the destination memory area + int st, status code +.status_codes : + DISK_IO_I_CALLED (signalled) + DISK_IO_E_OPEN + DISK_IO_E_GETC +.notes : + 1.1, 11-Feb-1998, creation + +.- */ +int ReadNibblesFromFile(const char *name, int size, Nibble *dest) +{ + FILE *f; + int i; + int by; + int st = DISK_IO_S_OK; + + debug1(DEBUG_C_TRACE, DISK_IO_I_CALLED, "ReadNibblesFromFile"); + + if((f = fopen(name, "rb")) == (FILE *)NULL) + { + ChfErrnoCondition; + ChfCondition st=DISK_IO_E_OPEN, CHF_ERROR, name ChfEnd; + } + + else + { + for(i=0; i> 4); + } + + (void)fclose(f); + } + + return st; +} + + +/* .+ + +.title : WriteNibblesToFile +.kind : C function +.creation : 11-Feb-1998 +.description : + This function writes 'size' nibbles taken from 'src' into the file 'name'. + It returns to the caller a status code + +.call : + st = WriteNibblesToFile(src, size, name); +.input : + const Nibble *src, pointer to data to be written + int size, # of nibble to write + const char *name, file name +.output : + int st, status code +.status_codes : + DISK_IO_I_CALLED (signalled) + DISK_IO_E_OPEN + DISK_IO_E_PUTC + DISK_IO_E_CLOSE +.notes : + 1.1, 11-Feb-1998, creation + +.- */ +int WriteNibblesToFile(const Nibble *src, int size, const char *name) +{ + FILE *f; + int i; + int by; + int st = DISK_IO_S_OK; + + debug1(DEBUG_C_TRACE, DISK_IO_I_CALLED, "WriteNibblesToFile"); + + if((f = fopen(name, "wb")) == (FILE *)NULL) + { + ChfErrnoCondition; + ChfCondition st=DISK_IO_E_OPEN, CHF_ERROR, name ChfEnd; + } + + else + { + for(i=0; i +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "disk_io.h" +#include "debug.h" + +#define CHF_MODULE_ID DISK_IO_CHF_MODULE_ID +#include + + +/* .+ + +.title : ReadObjectFromFile +.kind : C function +.creation : 10-Nov-2000 +.description : + This function reads an object from file 'name' and stores it into + the calculator's memory from nibble address 'start' inclusive to nibble + address 'end' exclusive. + + The presence of header 'hdr' in the disk file is checked, then the + header is stripped before starting the transfer. In 'hdr', '?' + is a wildcard character that matches any input character. + + Objects with an odd number of nibbles are padded to an even + size adding a 0 nibble to their end; the trailing zero *is* stored + in calculator's memory. + + When the object size exceeds the available space this function + transfers its head anyway, thereby corrupting the calculator's + memory between 'start' and 'end', except the first N_SAVE_AREA nibbles. + + This function returns to the caller a status code. + +.call : + st = ReadObjectFromFile(name, hdr, start, end); + +.input : + const char *name, input file name + const char *hdr, file header + Address start, start address (inclusive) + Address end, end address (exclusive) +.output : + int st, status code +.status_codes : + DISK_IO_I_CALLED (signalled) + DISK_IO_E_OPEN + DISK_IO_E_GETC + DISK_IO_E_BAD_HDR + DISK_IO_E_SIZE +.notes : + 3.14, 10-Nov-2000, creation + +.- */ +int ReadObjectFromFile( + const char *name, const char *hdr, Address start, Address end) +{ + size_t hdr_len = strlen(hdr); + FILE *f; + int i; + int by; + Address cur; + +#define N_SAVE_AREA 10 + Nibble save_area[N_SAVE_AREA]; + + int st = DISK_IO_S_OK; + + debug1(DEBUG_C_TRACE, DISK_IO_I_CALLED, "ReadObjectFromFile"); + + /* Save first nibbles of target space into save_area */ + for(cur=start, i=0; cur < end && i= end-1) + { + ChfCondition st=DISK_IO_E_SIZE, CHF_ERROR, name ChfEnd; + break; + } + + /* Store it */ + WriteNibble(cur++, (Nibble)(by & 0x0F)); + WriteNibble(cur++, (Nibble)((by & 0xF0) >> 4)); + } + + /* Check why getc() failed */ + if(ferror(f) && !feof(f)) + { + ChfErrnoCondition; + ChfCondition st=DISK_IO_E_GETC, CHF_ERROR, name ChfEnd; + } + + /* Recover from save_area if transfer failed */ + if(st) + for(cur=start, i=0; cur < end && i +#include +#include +#include /* 3.1: memset() */ + +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "display.h" +#include "x11.h" +#include "debug.h" + +#define CHF_MODULE_ID X11_CHF_MODULE_ID +#include + +#ifndef LCD_MAG +# define LCD_MAG 2 /* 4.1.1.1: Compat. default */ +#endif + +#define NIBBLES_PER_ROW 34 /* 136 pixel total */ +#define MAX_ROWS 64 /* 64 rows total */ +#define N_ANN 6 /* # of annunciators */ +#define LCD_X_ORIGIN 1+4*(LCD_MAG-1) /* x origin */ + +#if LCD_MAG==1 +# define LCD_Y_ORIGIN 14 /* y origin */ +#else +# define LCD_Y_ORIGIN 20 /* y origin */ +#endif + +/* 3.8: Origin and size of clip rectangle */ +#define LCD_CLIP_X_ORIGIN LCD_X_ORIGIN +#define LCD_CLIP_Y_ORIGIN 0 /* Don't clip annunciators */ +#define LCD_CLIP_WIDTH 131*LCD_MAG +#define LCD_CLIP_HEIGHT LCD_Y_ORIGIN+64*LCD_MAG + +#define MASK_ANN_LEFT 0x81 /* Annunciator's bit masks */ +#define MASK_ANN_RIGHT 0x82 +#define MASK_ANN_ALPHA 0x84 +#define MASK_ANN_BATTERY 0x88 +#define MASK_ANN_BUSY 0x90 +#define MASK_ANN_IO 0xA0 + + +/*--------------------------------------------------------------------------- + Static/Global variables + ---------------------------------------------------------------------------*/ + +static /*const*/ char nibble_bitmap_data[NIBBLE_VALUES][LCD_MAG] = +{ +#if LCD_MAG==1 + { 0x00 }, /* ---- */ + { 0x01 }, /* *--- */ + { 0x02 }, /* -*-- */ + { 0x03 }, /* **-- */ + { 0x04 }, /* --*- */ + { 0x05 }, /* *-*- */ + { 0x06 }, /* -**- */ + { 0x07 }, /* ***- */ + { 0x08 }, /* ---* */ + { 0x09 }, /* *--* */ + { 0x0a }, /* -*-* */ + { 0x0b }, /* **-* */ + { 0x0c }, /* --** */ + { 0x0d }, /* *-** */ + { 0x0e }, /* -*** */ + { 0x0f } /* **** */ +#elif LCD_MAG==2 + { 0x00, 0x00 }, /* ---- */ + { 0x03, 0x03 }, /* *--- */ + { 0x0c, 0x0c }, /* -*-- */ + { 0x0f, 0x0f }, /* **-- */ + { 0x30, 0x30 }, /* --*- */ + { 0x33, 0x33 }, /* *-*- */ + { 0x3c, 0x3c }, /* -**- */ + { 0x3f, 0x3f }, /* ***- */ + { 0xc0, 0xc0 }, /* ---* */ + { 0xc3, 0xc3 }, /* *--* */ + { 0xcc, 0xcc }, /* -*-* */ + { 0xcf, 0xcf }, /* **-* */ + { 0xf0, 0xf0 }, /* --** */ + { 0xf3, 0xf3 }, /* *-** */ + { 0xfc, 0xfc }, /* -*** */ + { 0xff, 0xff } /* **** */ +#else +# error "Bad LCD_MAG; supported values are 1 and 2" +#endif +}; + +static /*const*/ struct +{ + int mask; /* Bit mask */ + int x, y; /* Position */ + int w, h; /* Width, Height */ + char bitmap_data[24]; /* Bitmap data */ +} + +#define ANN_X(i) (8*LCD_MAG+(22*LCD_MAG+1)*i) +#define ANN_Y(i) (1+3*(LCD_MAG-1)) + +ann_data[N_ANN] = +{ + { MASK_ANN_LEFT, + ANN_X(0), ANN_Y(0), + 15, 12, + { 0xfe, 0x3f, 0xff, 0x7f, 0x9f, 0x7f, 0xcf, 0x7f, 0xe7, 0x7f, 0x03, 0x78, + 0x03, 0x70, 0xe7, 0x73, 0xcf, 0x73, 0x9f, 0x73, 0xff, 0x73, 0xfe, 0x33 + } + }, + { MASK_ANN_RIGHT, + ANN_X(1), ANN_Y(1), + 15, 12, + { 0xfe, 0x3f, 0xff, 0x7f, 0xff, 0x7c, 0xff, 0x79, 0xff, 0x73, 0x0f, 0x60, + 0x07, 0x60, 0xe7, 0x73, 0xe7, 0x79, 0xe7, 0x7c, 0xe7, 0x7f, 0xe6, 0x3f + } + }, + { MASK_ANN_ALPHA, + ANN_X(2), ANN_Y(2), + 15, 12, + { 0xe0, 0x03, 0x18, 0x44, 0x0c, 0x4c, 0x06, 0x2c, 0x07, 0x2c, 0x07, 0x1c, + 0x07, 0x0c, 0x07, 0x0c, 0x07, 0x0e, 0x0e, 0x4d, 0xf8, 0x38, 0x00, 0x00 + } + }, + { MASK_ANN_BATTERY, + ANN_X(3), ANN_Y(3), + 15, 12, + { 0x04, 0x10, 0x02, 0x20, 0x12, 0x24, 0x09, 0x48, 0xc9, 0x49, 0xc9, 0x49, + 0xc9, 0x49, 0x09, 0x48, 0x12, 0x24, 0x02, 0x20, 0x04, 0x10, 0x00, 0x00 + } + }, + { MASK_ANN_BUSY, + ANN_X(4), ANN_Y(4), + 15, 12, + { 0xfc, 0x1f, 0x08, 0x08, 0x08, 0x08, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, + 0x40, 0x01, 0x20, 0x02, 0x10, 0x04, 0xc8, 0x09, 0xe8, 0x0b, 0xfc, 0x1f + } + }, + { MASK_ANN_IO, + ANN_X(5), ANN_Y(5), + 15, 12, + { 0x0c, 0x00, 0x1e, 0x00, 0x33, 0x0c, 0x61, 0x18, 0xcc, 0x30, 0xfe, 0x7f, + 0xfe, 0x7f, 0xcc, 0x30, 0x61, 0x18, 0x33, 0x0c, 0x1e, 0x00, 0x0c, 0x00 + } + } + +}; + +static Nibble lcd_buffer[MAX_ROWS][NIBBLES_PER_ROW]; +static int ann_buffer; +static int clean; + +static Display *display; +static Window window; +static GC gc; +static unsigned long fg_pixel, bg_pixel; +static unsigned int depth; + +static Pixmap nibble_pixmap[NIBBLE_VALUES]; +static Pixmap ann_pixmap[N_ANN]; + + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : InitPixmaps +.kind : C function +.creation : 29-Jan-1998 +.description : + This function initializes the pixmaps for the Lcd screen elements and + stores them into the appropriate global variables. + +.call : + InitPixmaps(); +.input : + void +.output : + void +.status_codes : + X11_I_CALLED + X11_F_X_ERROR +.notes : + 1.1, 29-Jan-1998, creation + +.- */ +static void InitPixmaps(void) +{ + int i; + + debug1(DEBUG_C_TRACE, X11_I_CALLED, "InitPixmaps"); + + /* Initialize nibble_pixmap */ + for(i=0; i +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "display.h" +#include "x11.h" +#include "serial.h" +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID CPU_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Private macros / variables / functions + ---------------------------------------------------------------------------*/ + +#define T1_MULTIPLIER (8192/16) /* T2/T1 increment ratio */ +#define T1_INTERVAL 62500 /* us per T1 increment */ +#define T2_INTERVAL 122 /* us per T2 increment */ + +/* 3.1: MAX_IDLE_X_LOOP_TIMEOUT must be low enough to prevent overflow + of an int when computing the difference in microseconds between two + struct timeval and to avoid starvation of the serial port emulation + module (*); T1_MS_MULTIPLIER and T2_MS_DIVISOR are approximate + values used only to compute e reasonable timeout for IdleXLoop(); + the actual update of timers is carried out using T1_INTERVAL + and T2_INTERVAL, and should be more accurate. + + + (*) XXX This constraint will be removed when the serial port emulation + module will support asynchronous selection on pty fd and will + be able to interact with the GUI's select mechanism. +*/ +#define T1_MS_MULTIPLIER 63 /* 3.1: Milliseconds per T1 tick (~) */ +#define T2_MS_DIVISOR 8 /* 3.1: T2 ticks per millisecond (~) */ +#define MAX_IDLE_X_LOOP_TIMEOUT 1000 /* 3.1: Max timeout for IdleXLoop() */ + +#define T1_OVF_MASK NIBBLE_MASK /* 3.1: Timer overflow masks */ +#define T2_OVF_MASK 0xFFFFFFFF + +#define LCD_T1_MASK 0x3 /* LCD refresh timing mask */ +#define INT_T1_MASK 0xF /* Int. req. timing mask */ + +static int emulator_int_req = 0; /* Interrupt request flag */ + +/* This function contains the main emulator loop; under normal conditions, + it never returns to the caller. The only way to exit this function is + to signal a Chf condition that triggers an unwind operation. +*/ +static void EmulatorLoop(void) +{ + struct timeval old_t, cur_t; + int ela; + int inner_loop = cpu_status.inner_loop; + int t1_count = 0; + int i, j; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "EmulatorLoop"); + + /* Ignore past interrupt requests */ + emulator_int_req = 0; + + /* Get current time of day */ + gettimeofday(&old_t, NULL); + + while(1) + { + /* T1 loop */ + for(j=0; j= cpu_status.inner_loop_max) + { + inner_loop = cpu_status.inner_loop_max; + if(T1_INTERVAL > ela) usleep(T1_INTERVAL - ela); + } +#endif + + cpu_status.inner_loop = inner_loop; + old_t = cur_t; + } +} + + +/* Condition handler for the EmulatorLoop */ +static ChfAction EmulatorLoopHandler( + const ChfDescriptor *d, + const ChfState s, + ChfPointer ctx +) +{ + ChfAction act; + + /* Check Chf state */ + switch(s) + { + /* 2.1: Chf release 2 fixed the spelling of 'SIGNALING' */ + case CHF_SIGNALING: + /* ChfSignal() in progress */ + if(ChfGetModuleId(d) == CPU_CHF_MODULE_ID) + { + /* Condition from CPU modules; check Condition Code */ + switch(ChfGetConditionCode(d)) + { +#ifdef CPU_SPIN_SHUTDN + case CPU_I_SHUTDN: + /* CPU shutdown signalled with CPU_SPIN_SHUTDN defined; + Fatal error. + */ + ChfCondition CPU_F_BAD_SHUTDN, CHF_FATAL ChfEnd; + ChfSignal(); + + act = CHF_RESIGNAL; + break; +#else + case CPU_I_SHUTDN: + { + /* 3.1: CPU_SPIN_SHUTDN is not defined, and the cpu emulator + has just executed a shutdown instruction. + Let's do something a little tricky here: + + 1- redraw the LCD + + 2- handle serial port activities + + 3- determine which timer will expire first, and + compute an approximate value of the maximum duration + of the shutdown --> ms + + 4- handle serial port activities + + 5- enter the inner idle loop; it breaks when either an + X Event occurred (possibly clearing the shutdown) or + the shutdown timeout elapses + + 6- determine the actual time we spend in the idle loop + (X timeouts are not accurate enough for this purpose) + + 7- update T1 and T2, check their state and wake/interrupt + the CPU if necessary + + Activities 3-7 above are enclosed in an outer loop because we + cannot be absolutely sure of the actual time spent + in the idle loop; moreover, not all X Events actually + spool up the CPU. The outer loop breaks when the CPU is + actually brought out of shutdown. + + frac_t1 and frac_t2 contain the number of microseconds + not accounted for in the last T1/T2 update, respectively; + they help minimize the cumulative timing error induced + by executing the outer idle loop more than once. + */ + struct timeval start_idle, end_idle; + int frac_t1=0, frac_t2=0; + + gettimeofday(&start_idle, NULL); + + /* Redraw the LCD immediately before entering idle loop; + this ensures that the latest LCD updated actually + get to the screen. + */ + DrawLcd(); + + /* Handle serial port activity before entering the outer idle + loop, because this could possibly bring the cpu out of + shutdown right now. + */ + HandleSerial(); + + /* XXX + If either timer has a pending service request, + process it immediately. It is not clear why it was + not processed *before* shutdown, though. + */ + if(mod_status.hdw.t1_ctrl & T1_CTRL_SREQ) + { + if(mod_status.hdw.t1_ctrl & T1_CTRL_WAKE) + CpuWake(); + + if(mod_status.hdw.t1_ctrl & T1_CTRL_INT) + CpuIntRequest(INT_REQUEST_IRQ); + } + + if(mod_status.hdw.t2_ctrl & T2_CTRL_SREQ) + { + if(mod_status.hdw.t2_ctrl & T2_CTRL_WAKE) + CpuWake(); + + if(mod_status.hdw.t2_ctrl & T2_CTRL_INT) + CpuIntRequest(INT_REQUEST_IRQ); + } + + while(cpu_status.shutdn) + { + unsigned long ms = MAX_IDLE_X_LOOP_TIMEOUT; + unsigned long mst; + int ela; + int ela_ticks; + + debug3(DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T1 (during SHUTDN)", + mod_status.hdw.t1_ctrl, mod_status.hdw.t1_val); + debug3(DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T2 (during SHUTDN)", + mod_status.hdw.t2_ctrl, mod_status.hdw.t2_val); + + /* Determine which timer will expire first */ + if(mod_status.hdw.t1_ctrl & (T1_CTRL_INT|T1_CTRL_WAKE)) + { + /* T1 will do something on expiration */ + mst = ((unsigned long)mod_status.hdw.t1_val + 1) + * T1_MS_MULTIPLIER; + + debug2(DEBUG_C_TIMERS, CPU_I_TIMER_EXP, "T1", mst); + + if(mst < ms) ms = mst; + } + + if((mod_status.hdw.t2_ctrl & T2_CTRL_TRUN) + && (mod_status.hdw.t2_ctrl & (T2_CTRL_INT|T2_CTRL_WAKE))) + { + /* T2 is running and will do something on expiration */ + mst = ((unsigned long)mod_status.hdw.t2_val + 1) + / T2_MS_DIVISOR; + + debug2(DEBUG_C_TIMERS, CPU_I_TIMER_EXP, "T2", mst); + + if(mst < ms) ms = mst; + } + + /* Handle serial port activities at each iteration of + the outer idle loop; this ensures that the serial + port emulation will not starve. + */ + HandleSerial(); + + /* Enter idle loop, possibly with timeout; + The loop breaks when: + - any X Event occurs (possibly clearing the shutdown) + - the given timeout expires + */ + debug1(DEBUG_C_TIMERS, CPU_I_IDLE_X_LOOP, ms); + IdleXLoop(ms); + + /* End of idle loop; compute actual elapsed time */ + gettimeofday(&end_idle, NULL); + + ela = (end_idle.tv_sec - start_idle.tv_sec) * 1000000 + + (end_idle.tv_usec - start_idle.tv_usec); + + /* Update start_idle here to contain lag */ + start_idle = end_idle; + + debug1(DEBUG_C_TIMERS, CPU_I_ELAPSED, ela); + + /* Update timers and act accordingly */ + ela_ticks = ((ela+frac_t1) + T1_INTERVAL/2) / T1_INTERVAL; + frac_t1 = (ela+frac_t1) - ela_ticks * T1_INTERVAL; + + if(ela_ticks > mod_status.hdw.t1_val) + { + debug1(DEBUG_C_TIMERS, CPU_I_TIMER1_EX, + mod_status.hdw.t1_ctrl); + + mod_status.hdw.t1_ctrl |= T1_CTRL_SREQ; + + if(mod_status.hdw.t1_ctrl & T1_CTRL_WAKE) + CpuWake(); + + if(mod_status.hdw.t1_ctrl & T1_CTRL_INT) + CpuIntRequest(INT_REQUEST_IRQ); + } + + mod_status.hdw.t1_val = + (mod_status.hdw.t1_val - ela_ticks) & T1_OVF_MASK; + + if(mod_status.hdw.t2_ctrl & T2_CTRL_TRUN) + { + ela_ticks = ((ela+frac_t2) + T2_INTERVAL/2) / T2_INTERVAL; + frac_t2 = (ela+frac_t2) - ela_ticks * T2_INTERVAL; + + if(ela_ticks > mod_status.hdw.t2_val) + { + debug1(DEBUG_C_TIMERS, CPU_I_TIMER2_EX, + mod_status.hdw.t2_ctrl); + + mod_status.hdw.t2_ctrl |= T2_CTRL_SREQ; + + if(mod_status.hdw.t2_ctrl & T2_CTRL_WAKE) + CpuWake(); + + if(mod_status.hdw.t2_ctrl & T2_CTRL_INT) + CpuIntRequest(INT_REQUEST_IRQ); + } + + mod_status.hdw.t2_val = + (mod_status.hdw.t2_val - ela_ticks) & T2_OVF_MASK; + } + } + + debug3(DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T1 (after SHUTDN)", + mod_status.hdw.t1_ctrl, mod_status.hdw.t1_val); + debug3(DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T2 (after SHUTDN)", + mod_status.hdw.t2_ctrl, mod_status.hdw.t2_val); + + act = CHF_CONTINUE; + } + break; +#endif + + case CPU_I_EMULATOR_INT: + /* Emulator interrupt; unwind */ + act = CHF_UNWIND; + break; + + default: + /* Condition Code not handled; resignal */ + act = CHF_RESIGNAL; + } + } + + else + /* Condition from other modules; resignal */ + act = CHF_RESIGNAL; + + break; + + default: + /* Other states; resignal the condition */ + act = CHF_RESIGNAL; + break; + } + + return act; +} + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : Emulator +.kind : C function +.creation : 17-Feb-1998 +.description : + This function implements the main emulator loop. For efficiency reasons, + it also emulates both T1 and T2 timers. Under normal conditions, this + function returns to the caller only when an emulator interrupt request + has been posted using EmulatorIntRequest(). + + The only way to exit this function (with a non-local jump) is to signal + a Chf Condition that triggers an unwind operation. + +.call : + Emulator(); +.input : + void +.output : + void +.status_codes : + CPU_I_CALLED + CPU_I_TIMER1_EX + CPU_I_TIMER2_EX + Other conditions signalled by lower level modules +.notes : + 1.1, 17-Feb-1998, creation + +.- */ +void Emulator(void) +{ + jmp_buf unwind_context; + + debug1(DEBUG_C_TRACE, CPU_I_CALLED, "Emulator"); + + /* Setup unwind_context */ + if(setjmp(unwind_context) == 0) + { + /* Push condition handler, with NULL context */ + ChfPushHandler(EmulatorLoopHandler, &unwind_context, (ChfPointer)NULL); + + /* Activate emulator loop */ + EmulatorLoop(); + } + + else + { + /* Unwinding after an emulator interrupt */ + } +} + + +/* .+ + +.title : EmulatorIntRequest +.kind : C function +.creation : 18-Feb-1998 +.description : + This function posts an interrupt request for the running emulator loop. + The request will be satisfied as soon as possible and Emulator() will + return to the caller. + +.call : + EmulatorIntRequest(); +.input : + void +.output : + void +.status_codes : + * +.notes : + 1.1, 18-Feb-1998, creation + +.- */ +void EmulatorIntRequest(void) +{ + emulator_int_req = 1; +} + + +/* .+ + +.title : EmulatorInit +.kind : C function +.creation : 8-Sep-2000 +.description : + This function initializes the cpu and modules emulator subsystems; + if the reset emulator option is set, a reset is forced on both + subsystems, too. + +.call : + EmulatorInit(); +.input : + void +.output : + void +.status_codes : + * Status codes signaled by CpuInit() and CpuReset() + * Status codes signaled by ModInit() and ModReset() +.notes : + 2.1, 8-Sep-2000, creation + 2.4, 12-Sep-2000, update + - invoke CpuInit() before ModInit() so that interrupt requests + generated by ModInit() are honored as they should. + 3.2, 21-Sep-2000, update: + - now invoking ModSelectDescription(args.hw) to select and + register an appropriate module description table depending on + args.hw option. + +.- */ +void EmulatorInit(void) +{ + /* Select a module description table */ + ModSelectDescription(args.hw); + + /* Initialize cpu and modules subsystems */ + CpuInit(); + ModInit(); + + /* Reset if appropriate */ + if(args.reset) + { + CpuReset(); + ModReset(); + } +} + + +/* .+ + +.title : EmulatorExit +.kind : C function +.creation : 8-Sep-2000 +.description : + This function prepares the emulator to exit. If 'opt' is SAVE_AND_EXIT, + it also attempts to save the emulator's state on mass storage. Notice + that this function never exits the application directly, but always + returns to the caller unless an unrecoverable error occurs. + +.call : + EmulatorExit(opt); +.input : + enum ExitOption opt, emulator exit option +.output : + void +.status_codes : + * Status codes signaled by CpuSave() and ModSave() +.notes : + 2.1, 8-Sep-2000, creation + +.- */ +void EmulatorExit(enum ExitOption opt) +{ + switch(opt) + { + + case SAVE_AND_EXIT: + /* Save state of cpu and modules subsystems */ + ModSave(); + CpuSave(); + break; + + default: + /* Default behavior; do nothing */ + break; + } +} diff --git a/flash49.c b/flash49.c new file mode 100644 index 0000000..b6ae34a --- /dev/null +++ b/flash49.c @@ -0,0 +1,525 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: flash49.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: flash49.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 25-Sep-2000 +.keywords : * +.description : + This module emulates the Internal Flash Rom of the HP49. + + References: + + 28F160S5/28F320S5 Data Sheet, by Intel Corp. + +.include : config.h, machdep.h, cpu.h, modules.h flash49.h + +.notes : + $Log: flash49.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:40 cibrario + Added/Replaced GPL header + + Revision 3.5 2000/10/02 09:49:18 cibrario + Linux support: + - added a default case in StoreData()'s switch; this is cleaner, and + makes gcc happier. + + Revision 3.3 2000/09/26 15:30:14 cibrario + *** empty log message *** + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: flash49.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "flash49.h" +#include "debug.h" + +#include "args.h" + +#define CHF_MODULE_ID FLASH_CHF_MODULE_ID +#include + + + +/*--------------------------------------------------------------------------- + Private Macro/Data type definitions + ---------------------------------------------------------------------------*/ + +#define BLOCK_SIZE 0x10000 +#define BLOCK_BASE_MASK 0xFFFF + +#define ByteAddress(address) ((address) >> 1) +#define NibbleAddress(address) ((address) << 1) +#define BlockBase(address) ((address) & ~BLOCK_BASE_MASK) +#define IsOdd(address) ((address) & 0x1) +#define LowNibble(d) ((Nibble)((d) & NIBBLE_MASK)) +#define HighNibble(d) ((Nibble)(((d) >> 4) & NIBBLE_MASK)) +#define ShiftHigh(d) ((d) << 4) + + +/* Flash cycle types */ +enum FlashCycle +{ + FLASH_CYCLE_READ = 0, + FLASH_CYCLE_WRITE, + + FLASH_CYCLE_N /* Total # of cycle types */ +}; + +/* State transition function */ +typedef int (*FlashF) + (enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data); + + + +/*--------------------------------------------------------------------------- + Private state variables + ---------------------------------------------------------------------------*/ + +/* External storage */ +extern struct ModStatus_49 *mod_status_49; + +static int r_buffer; /* Nibble buffer during read */ +static int w_buffer; /* Nibble buffer during write */ +static enum FlashState fsm_state; /* FSM state */ + +/* Write buffer */ +#define WB_COUNT_MASK 0x1F +#define WB_SIZE 0x20 +static int wb_count; /* Counter for Write to Buffer */ +static int wb_cdown; /* Count down */ +static XAddress wb_start; /* Start address for Write to Buffer */ +static int wb[WB_SIZE]; /* Write buffer */ + + + +/*--------------------------------------------------------------------------- + State transition private functions + ---------------------------------------------------------------------------*/ + + +/* This function is called by default for unhandled state transitions */ +static int BadCommand(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + /* Unknown command: signal and reset state to FLASH_ST_READ_ARRAY */ + ChfCondition FLASH_W_BAD_CMD, CHF_WARNING, + *state, cycle, address, data + ChfEnd; + ChfSignal(); + + *state = FLASH_ST_READ_ARRAY; + return 0; /* Dummy result */ +} + + +/* This function is called to read the Flash Rom array */ +static int ReadArray(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + /* Read a byte from the array; no state transitions */ + return + mod_status_49->flash[NibbleAddress(address)] + | ShiftHigh(mod_status_49->flash[NibbleAddress(address)+1]); +} + + +/* This function is called to parse the first byte of any command */ +static int ParseCommand(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + switch(data) + { + case FLASH_CMD_READ_ARRAY: + /* Transition to FLASH_ST_READ_ARRAY state */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Read Array"); + *state = FLASH_ST_READ_ARRAY; + break; + + case FLASH_CMD_CLR_SR: + /* Clear status register; section 4.5 on Data Sheet. + The current implementation does nothing, because + the value of the status register is fixed. No state transitions. + */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Clear Status"); + break; + + case FLASH_CMD_WRITE_BUFFER: + /* Write to Buffer; section 4.8 on Data Sheet. + Transition to FLASH_ST_READ_XSR state. + */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Write to Buffer (start)"); + *state = FLASH_ST_READ_XSR; + break; + + case FLASH_CMD_READ_SR: + /* Read Status; section 4.4 on Data Sheet. + Transition to FLASH_ST_READ_SR state. + */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Read Status"); + *state = FLASH_ST_READ_SR; + break; + + case FLASH_CMD_BL_ERASE: + /* Block Erase; section 4.6 on Data Sheet. + Transition to FLASH_ST_BL_ERASE state. + Consistency of block addresses is not checked. + */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Erase Block (start)"); + *state = FLASH_ST_BL_ERASE; + break; + + default: + /* Unknown command; signal, ignore, keep current state. */ + ChfCondition FLASH_W_BAD_CMD, CHF_WARNING, + *state, cycle, address, data + ChfEnd; + ChfSignal(); + break; + } + + return 0; /* No result; this is a write cycle */ +} + + +/* This function returns to the caller the value of XSR */ +static int ReadXSR(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + /* Return XSR status; a buffer is always available in the current + emulation scheme. Keep current state. + */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Read XSR"); + return FLASH_XSR_WBS; +} + + +/* This function returns to the caller the value of SR */ +static int ReadSR(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + /* Return SR status; the WSM executes in zero time in the current + emulation scheme. Keep current state. + */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Read SR"); + return FLASH_SR_WSMS; +} + + +/* This function is called to store the WRITE_BUFFER byte count; + both wb_count and wb_cdown are set; StoreData() decrements the + latter, WriteConfirm() uses the former to determine how many bytes + must write to the Flash array. +*/ +static int StoreCount(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + /* Store WRITE_BUFFER count; next state is FLASH_ST_WRITE_DATA */ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Write to Buffer (count)"); + wb_count = wb_cdown = data & WB_COUNT_MASK; + + *state = FLASH_ST_WRITE_DATA_1; + return 0; /* No result; this is a write cycle */ +} + + +/* This function is called to store a byte into the write buffer. + The first write cycle also sets the buffer's base address (wb_start). + The function transitions to state FLASH_ST_WRITE_CONFIRM when all + bytes have been stored. +*/ +static int StoreData(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + int index; + + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Write to Buffer (data)"); + + /* Store WRITE_BUFFER data; the first write also stores the + buffer starting address. + */ + switch(*state) + { + + case FLASH_ST_WRITE_DATA_1: + wb_start = address; + wb[0] = data; + *state = FLASH_ST_WRITE_DATA_N; + break; + + case FLASH_ST_WRITE_DATA_N: + index = address - wb_start; + if(index < 0 || index >= WB_SIZE) + { + ChfCondition FLASH_W_BAD_ADDRESS, CHF_WARNING, + *state, cycle, address, data + ChfEnd; + ChfSignal(); + } + else + wb[index] = data; + break; + + default: + *state = FLASH_ST_READ_ARRAY; + break; + } + + if(--wb_cdown < 0) + *state = FLASH_ST_WRITE_CONFIRM; + + return 0; /* No result; this is a write cycle */ +} + + +/* This function expects a Write to Buffer confirmation command + (FLASH_CMD_WRITE_BUFFER_2); if it is received, the write buffer + is copied into the Flash Rom array, otherwise the write cycle is + aborted. In both cases, the new state is FLASH_ST_READ_ARRAY. +*/ +static int WriteConfirm(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Write to Buffer (end)"); + + /* Expect Write to Buffer confirmation code */ + if(data == FLASH_CMD_WRITE_BUFFER_2) + { + int i; + + /* Confirmation OK; write. + Remember that wb_count is the byte count MINUS 1. + */ + for(i=0; i<=wb_count; i++) + { + mod_status_49->flash[NibbleAddress(wb_start+i)] + = LowNibble(wb[i]); + mod_status_49->flash[NibbleAddress(wb_start+i)+1] + = HighNibble(wb[i]); + } + } + + *state = FLASH_ST_READ_ARRAY; + return 0; /* No result */ +} + + +/* If the FLASH_CMD_BL_ERASE_2 command is received, this function erases + the block pointed by the current address; otherwise the block erase + operation is aborted. + In both cases, the new state is FLASH_ST_READ_SR. +*/ +static int BlockErase(enum FlashState *state, enum FlashCycle cycle, + XAddress address, int data) +{ + debug1(DEBUG_C_FLASH, FLASH_I_FSM_OP, "Block Erase (end)"); + + /* Expect Write to Buffer confirmation code */ + if(data == FLASH_CMD_BL_ERASE_2) + { + XAddress block_base = BlockBase(address); + int i; + + /* Confirmation OK; erase */ + for(i=0; iflash[ + NibbleAddress(block_base+i)] = (Nibble)0xF; + mod_status_49->flash[ + NibbleAddress(block_base+i)+1] = (Nibble)0xF; + } + } + + *state = FLASH_ST_READ_SR; + return 0; /* No result */ +} + + + +/*--------------------------------------------------------------------------- + FSM state diagram; two-dimensional array of FlashF; each function + is invoked when the FSM is in a given state (first index), and + a particular cycle (second index) is requested. + ---------------------------------------------------------------------------*/ + +static FlashF F[FLASH_ST_N][FLASH_CYCLE_N] = +{ + { ReadArray, ParseCommand }, /* FLASH_ST_READ_ARRAY */ + { ReadSR, ParseCommand }, /* FLASH_ST_READ_SR */ + { ReadXSR, StoreCount }, /* FLASH_ST_READ_XSR */ + { BadCommand, StoreData }, /* FLASH_ST_WRITE_DATA_1 */ + { BadCommand, StoreData }, /* FLASH_ST_WRITE_DATA_N */ + { BadCommand, WriteConfirm }, /* FLASH_ST_WRITE_CONFIRM */ + { BadCommand, BlockErase } /* FLASH_ST_BL_ERASE */ +}; + + + +/*--------------------------------------------------------------------------- + Other private functions + ---------------------------------------------------------------------------*/ + +/* This function invokes the FSM to execute the given 'cycle', + with 'address' and 'data' as arguments. Returns the + result of the FSM, if any. +*/ +static int FSM(enum FlashCycle cycle, XAddress address, int data) +{ + int result; + + debug2(DEBUG_C_FLASH, FLASH_I_FSM, fsm_state, cycle); + debug2(DEBUG_C_FLASH, FLASH_I_FSM_AD, address, data); + + result = F[fsm_state][cycle](&fsm_state, cycle, address, data); + + debug2(DEBUG_C_FLASH, FLASH_I_FSM_RESULT, fsm_state, result); + return result; +} + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : FlashRead49 +.kind : C function +.creation : 25-Sep-2000 +.description : + This function reads the nibble @address from Flash Rom and returns it + to the caller. + + This function DOES NOT supports even/odd nibble accesses at non-contiguous + addresses. + +.call : + n = FlashRead49(address); +.input : + XAddress address +.output : + Nibble n +.status_codes : + FLASH_I_READ + FLASH_I_FSM_OP + FLASH_W_BAD_CMD +.notes : + 3.3, 25-Sep-2000, creation + +.- */ +Nibble FlashRead49(XAddress address) +{ + Nibble result; + + if(IsOdd(address)) + /* Odd address, return buffered data from previous read */ + result = HighNibble(r_buffer); + + else + { + /* Even address, invoke FSM */ + r_buffer = FSM(FLASH_CYCLE_READ, ByteAddress(address), 0); + result = LowNibble(r_buffer); + } + + + debug2(DEBUG_C_TRACE|DEBUG_C_FLASH, FLASH_I_READ, address, result); + + return result; +} + + +/* .+ + +.title : FlashWrite49 +.kind : C function +.creation : 25-Sep-2000 +.description : + This function writes nibble datum @address into Flash Rom. + + This function DOES NOT supports even/odd nibble accesses at non-contiguous + addresses. + +.call : + FlashWrite49(address, datum); +.input : + XAddress address + Nibble datum +.output : + void +.status_codes : + FLASH_I_WRITE + FLASH_I_FSM_OP + FLASH_W_BAD_CMD + FLASH_W_BAD_ADDRESS +.notes : + 3.3, 25-Sep-2000, creation + +.- */ +void FlashWrite49(XAddress address, Nibble datum) +{ + debug2(DEBUG_C_TRACE|DEBUG_C_FLASH, FLASH_I_WRITE, address, datum); + + if(IsOdd(address)) + /* Odd address, invoke FSM; ignore result */ + FSM(FLASH_CYCLE_WRITE, ByteAddress(address), + w_buffer|ShiftHigh(datum)); + + else + /* Even address, buffer datum */ + w_buffer = datum; + +} diff --git a/flash49.h b/flash49.h new file mode 100644 index 0000000..b13c9a9 --- /dev/null +++ b/flash49.h @@ -0,0 +1,164 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + + +/* .+ + +.identifier : $Id: flash49.h,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: flash49.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 25-Sep-2000 +.keywords : * +.description : + This header contains all definitions and declarations related to the + Internal Flash ROM emulation module of the HP49. References: + + Known deficiencies of the current Flash ROM emulation: + + - some (many) commands are not emulated, even BCS ones. + - program/erase times are not emulated + - suspension is not supported + + References: + + 28F160S5/28F320S5 Data Sheet, by Intel Corp. + +.include : config.h machdep.h cpu.h modules.h + +.notes : + $Log: flash49.h,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:41 cibrario + Added/Replaced GPL header + + Revision 3.3 2000/09/26 15:30:07 cibrario + *** empty log message *** + + +.- */ + + +/*--------------------------------------------------------------------------- + Macro/Data type definitions + ---------------------------------------------------------------------------*/ + +#define FLASH49_RCS_INFO "$Revision: 4.1 $ $State: Rel $" + + +/* Command Set Definitions, Table 3 on Data Sheet. + Both BCS and SCS commands are listed here; commands marked with + (*) are not implemented. + + The last fixed byte of multibyte commands has a '_2' suffix. +*/ +#define FLASH_CMD_READ_ARRAY 0xFF /* BCS */ +#define FLASH_CMD_READ_ID 0x90 /* BCS (*) */ +#define FLASH_CMD_READ_QUERY 0x98 /* SCS (*) */ +#define FLASH_CMD_READ_SR 0x70 /* BCS */ +#define FLASH_CMD_CLR_SR 0x50 /* BCS */ +#define FLASH_CMD_WRITE_BUFFER 0xE8 /* SCS */ +# define FLASH_CMD_WRITE_BUFFER_2 0xD0 +#define FLASH_CMD_BW_PGM 0x40 /* BCS (*) */ +#define FLASH_CMD_BW_PGM_ALT 0x10 /* BCS, alternate (*) */ +#define FLASH_CMD_BL_ERASE 0x20 /* BCS */ +# define FLASH_CMD_BL_ERASE_2 0xD0 +#define FLASH_CMD_SUSPEND 0xB0 /* BCS (*) */ +#define FLASH_CMD_RESUME 0xD0 /* BCS (*) */ +#define FLASH_CMD_STS_CONFIG 0xB8 /* SCS (*) */ +#define FLASH_CMD_BL_LB 0x60 /* SCS (*) */ +# define FLASH_CMD_BL_LB_SET 0x01 +# define FLASH_CMD_BL_LB_CLR 0xD0 +#define FLASH_CMD_CHIP_ERASE 0x30 /* SCS (*) */ +# define FLASH_CMD_CHIP_ERASE_2 0xD0 + + +/* Status Register bit masks, Table 15 on Data Sheet +*/ +#define FLASH_SR_WSMS 0x80 /* WSM state, 0=busy, 1=ready */ +#define FLASH_SR_ESS 0x40 /* Erase suspend, 1=suspended */ +#define FLASH_SR_ECLBS 0x20 /* 1=Error during erasure */ +#define FLASH_SR_BWSLBS 0x10 /* 1=Error during program */ +#define FLASH_SR_VPPS 0x08 /* 1=Vpp error */ +#define FLASH_SR_BWSS 0x04 /* Program suspend, 1=suspended */ +#define FLASH_SR_DPS 0x02 /* 1=Lock encountered */ + + +/* Extended Status Register bit masks, Table 16 on Data Sheet +*/ +#define FLASH_XSR_WBS 0x80 /* Write buffer status 1=available */ + + +/* State of the Flash FSM, derived from command descriptions on + pages 16...28 and from flowcharts on Figure 6...12 of Data Sheet +*/ +enum FlashState +{ + FLASH_ST_READ_ARRAY = 0, /* Read Array after CMD_READ_ARRAY */ + FLASH_ST_READ_SR, /* Read Status Reg. after CMD_READ_SR */ + FLASH_ST_READ_XSR, /* Read XSR after CMD_WRITE_BUFFER */ + FLASH_ST_WRITE_DATA_1, /* Write data after ST_WRITE_COUNT */ + FLASH_ST_WRITE_DATA_N, /* Write data after first write */ + FLASH_ST_WRITE_CONFIRM, /* Write confirmation after (ST_WRITE_DATA)* */ + FLASH_ST_BL_ERASE, /* Block erase started */ + FLASH_ST_N /* Total # of FSM states */ +}; + + +/*--------------------------------------------------------------------------- + Chf condition codes + ---------------------------------------------------------------------------*/ + +#define FLASH_I_READ 101 /* Read from address %x: %d */ +#define FLASH_I_WRITE 102 /* Write address %x, datum %x */ +#define FLASH_I_FSM 103 /* FSM from state %d, cycle %d */ +#define FLASH_I_FSM_AD 104 /* FSM address %x, data %x */ +#define FLASH_I_FSM_RESULT 105 /* FSM next state %d, result %x */ +#define FLASH_I_FSM_OP 106 /* FSM operation %s */ +#define FLASH_W_BAD_CMD 201 /* Bad cmd st%d, cycle%d, a%x, d%d */ +#define FLASH_W_BAD_ADDRESS 202 /* Bad addr st%d, cycle%d, a%x, d%d */ +#define FLASH_E_xxx 301 +#define FLASH_F_xxx 401 + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/* Read/Write operations, nibble-by-nibble */ +Nibble FlashRead49(XAddress address); +void FlashWrite49(XAddress address, Nibble datum); diff --git a/flash49.msf b/flash49.msf new file mode 100644 index 0000000..c57c0cc --- /dev/null +++ b/flash49.msf @@ -0,0 +1,38 @@ +$ .+ +$ .identifier : $Id: flash49.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HPxx emulator +$ .title : $RCSfile: flash49.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 25-Sep-2000 +$ .keywords : * +$ .description : +$ Message catalog source file for the flash rom emulator. +$ .notes : +$ . $Log: flash49.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.8 2000/10/19 15:00:41 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 3.3 2000/09/26 15:30:28 cibrario +$ *** empty log message *** +$ .- + +$set 1 +16 Flash + +$set 16 +101 Read (Nibble) A[%08X] D[%01X] +102 Write (Nibble) A[%08X] D[%01X] +103 FSM STATE[%d] CYCLE[%d] +104 \tFSM Args: (Byte)A[%08X] D[%02X] +105 \tFSM Next: STATE[%d] RESULT[%02X] +106 \tFSM Operation: [%s] +201 Command unknown/not implemented - FSM Info:\n\ +\tSTATE[%d] CYCLE[%d] A[%08X] D[%02X] +202 Invalid address in Write to Buffer - FSM Info:\n\ +\tSTATE[%d] CYCLE[%d] A[%08X] D[%02X] diff --git a/gpl.texi b/gpl.texi new file mode 100644 index 0000000..7fd7361 --- /dev/null +++ b/gpl.texi @@ -0,0 +1,392 @@ +@node GNU GENERAL PUBLIC LICENSE, Concept Index, Tips Tricks and Known Bugs, Top +@unnumbered GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@c This file is intended to be included in another file. + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate 0 +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) 19@var{yy} @var{name of author} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gpl_replace b/gpl_replace new file mode 100755 index 0000000..7546832 --- /dev/null +++ b/gpl_replace @@ -0,0 +1,78 @@ +#!/bin/csh +# +# $Id: gpl_replace,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +# +# This script adds/replaces the GPL header $gpl_template in +# all .c and .h files in the RCS repository. Must be executed on +# a clean (no locked files) repository. +# +# Arguments: +# +# argv[1]: -r argument to ci +# argv[2]: -m argument to ci, default provided + +if( $#argv < 1 ) then + echo "gpl_replace: missing target release number" + exit 1 +endif + +if( $#argv == 1 ) then + set argv = ($argv "Added/Replaced GPL header") +endif + +set gpl_template = gpl_template +set rcs_flist = `ls RCS/*.[ch],v` +set exit_code = 0 + +co $gpl_template +if( $status != 0 ) then + echo "gpl_replace: co ${gpl_template} failed" + exit 9 +endif + +foreach rcs_file ($rcs_flist) + set file = `basename $rcs_file ,v` + set s_file = "${file}_s" + set j_file = "${file}_j" + set b_file = "${file}_b" + + # Extract file from repository + co -l $file + if( $status != 0 ) then + echo "gpl_replace: ${file}: checkout failed; edit manually" + set exit_code = 2 + continue + endif + + sed -e '/.*+-+.*/,$\!d' $file > $s_file + + if( -z $s_file ) then + # File does not contain the template yet; add + cat $gpl_template - $file > $j_file < $j_file < +#include +#include +#include /* 3.1: memset() */ +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "disk_io.h" +#include "serial.h" /* 2.5: Serial port emulation module */ +#include "x_func.h" /* 3.13: Extended emulator functions */ +#include "debug.h" + +#include "args.h" + +#define CHF_MODULE_ID MOD_CHF_MODULE_ID +#include + + +static const int addr_mask[] = { 0x0000F, 0x000F0, 0x00F00, 0x0F000, 0xF0000 }; + +static const int32 int32_mask[] = +{ 0x0000000F, 0x000000F0, 0x00000F00, 0x0000F000, + 0x000F0000, 0x00F00000, 0x0F000000, 0xF0000000 }; + + +/* .+ + +.title : HdwInit +.kind : C function +.creation : 23-Jan-1998 +.description : + This function initializes the Hdw module, restoring the status of the + peripheral devices associated to it from disk. + +.call : + HdwInit(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_W_HDW_INIT +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, bug fix: + memset() invocation was improper, and could lead to memory corruption + +.- */ +void HdwInit(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "HdwInit"); + + if(ReadStructFromFile(args.hdw_file_name, sizeof(mod_status.hdw), + &mod_status.hdw)) + { + ChfCondition MOD_W_HDW_INIT, CHF_WARNING ChfEnd; + ChfSignal(); + + (void)memset(&mod_status.hdw, 0, sizeof(mod_status.hdw)); + } +} + + +/* .+ + +.title : HdwSave +.kind : C function +.creation : 11-Feb-1998 +.description : + This function saves the current status of the peripheral devices associated + to the Hdw module to disk. + +.call : + HdwSave(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_HDW_SAVE +.notes : + 1.1, 11-Feb-1998, creation + +.- */ +void HdwSave(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "HdwSave"); + + if(WriteStructToFile(&mod_status.hdw, sizeof(mod_status.hdw), + args.hdw_file_name)) + { + ChfCondition MOD_E_HDW_SAVE, CHF_ERROR ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : HdwRead +.kind : C function +.creation : 23-Jan-1998 +.description : + This function reads a nibble from the Hdw module. + +.call : + d = HdwRead(rel_address); +.input : + Address rel_address, relative address +.output : + Nibble d, data +.status_codes : + MOD_I_CALLED + MOD_W_HDW_READ +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, update + - read from rel_address 0x0F now returns the current value of + mod_status.hdw.card_status; its value is set during the + initialization of other peripheral modules. + 2.5, 14-Sep-2000, update + - added support for serial port emulation + +.- */ +Nibble HdwRead(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "HdwRead"); + + /* In the following switch, each case corresponds to one hdw register. + If the register must be read from the shadow space mod_status.hdw.hdw[], + simply put a break in the case, otherwise code any special action for + the register and end the case with a return. + */ + switch(rel_address) + { + case 0x00: /* LCD driver registers */ + case 0x01: + case 0x02: + case 0x03: + case 0x0B: + case 0x0C: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + break; + + case 0x04: /* CRC register */ + case 0x05: + case 0x06: + case 0x07: + return (Nibble)((mod_status.hdw.crc >> ((rel_address-0x04)*4)) & 0x0F); + + case 0x08: /* Power status */ + /* No power status related interrupt have occoured */ + return (Nibble)0; + + case 0x09: /* Power control */ + break; + + case 0x0D: /* Serial port baud-rate register */ + break; + + case 0x10: /* Serial port interrupt and I/O control register */ + return Serial_IOC_Read(); + + case 0x11: /* Serial port receiver control/status register */ + return Serial_RCS_Read(); + + case 0x12: /* Serial port transmitter control/status register */ + return Serial_TCS_Read(); + + /* Serial port receiver buffer register; the actual read takes place + when the LS nibble is read; serial_rbr buffers the MS nibble. + */ + case 0x14: + return (mod_status.hdw.serial_rbr = Serial_RBR_Read()) & 0x0F; + + case 0x15: + return (mod_status.hdw.serial_rbr >> 4) & 0x0F; + + case 0x0E: /* Card interface */ + break; + + case 0x0F: /* Card interface */ + /* 2.4: Return current card status */ + return mod_status.hdw.card_status; + + case 0x18: /* Service request */ + case 0x19: + break; + + case 0x1A: /* IR registers */ + case 0x1C: + case 0x1D: + break; + + case 0x1B: /* Base nibble offset */ + break; + + case 0x1E: /* Scratch pad */ + break; + + case 0x1F: /* Base Nibble */ + break; + + case 0x2E: /* Timer 1 Control */ + return mod_status.hdw.t1_ctrl; + + case 0x2F: /* Timer 2 Control */ + return mod_status.hdw.t2_ctrl; + +#ifdef HP49_SUPPORT + /* 3.2: The HP49 firmware (1.19-4) reads a nibble from 0x30 */ + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + return (Nibble)0x0; +#endif + + case 0x37: /* Timer 1 value */ + return mod_status.hdw.t1_val; + + case 0x38: /* Timer 2 value */ + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + return + (Nibble)((mod_status.hdw.t2_val >> ((rel_address-0x38)*4)) & 0x0F); + + default: + ChfCondition MOD_W_HDW_READ, CHF_WARNING, rel_address ChfEnd; + ChfSignal(); + return (Nibble)0xF; + } + + /* Read from hdw register array */ + return mod_status.hdw.hdw[rel_address]; +} + + +/* .+ + +.title : HdwWrite +.kind : C function +.creation : 23-Jan-1998 +.description : + This function writes a nibble to the Hdw module. + +.call : + HdwWrite(rel_address, data); +.input : + Address rel_address, relative address + Nibble data, data to be written +.output : + void +.status_codes : + MOD_I_CALLED + MOD_W_HDW_WRITE +.notes : + 1.1, 23-Jan-1998, creation + 2.5, 14-Sep-2000, update + - added support for serial port emulation + +.- */ +void HdwWrite(Address rel_address, Nibble data) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "HdwWrite"); + + /* This switch has a case for each 'known' hdw register. The code inside the + case performs the actions specific for that register; the code following + the switch, instead, simply takes care to shadow the hdw register into + the mod_status.hdw.hdw[] array + */ + switch(rel_address) + { + case 0x00: /* LCD horizontal offset, LCD enable flag */ + mod_status.hdw.lcd_offset = (int)data & 0x07; + mod_status.hdw.lcd_on = ((data & 0x08) != 0); + break; + + case 0x01: /* LCD contrast, LS nibble */ + mod_status.hdw.lcd_contrast &= 0x10; + mod_status.hdw.lcd_contrast |= (int)data; + break; + + case 0x02: /* LCD contrast, MS bit */ + mod_status.hdw.lcd_contrast &= 0x0F; + mod_status.hdw.lcd_contrast |= (((int)data & 0x01) << 4); + break; + + case 0x03: /* LCD test control */ + break; + + case 0x04: /* CRC register */ + case 0x05: + case 0x06: + case 0x07: + mod_status.hdw.crc &= ~addr_mask[rel_address-0x04]; + mod_status.hdw.crc |= ((int)data << ((rel_address-0x04)*4)); + break; + + case 0x08: /* Power status and power control */ + case 0x09: + break; + + case 0x0B: /* LCD annunciator control (low nibble) */ + mod_status.hdw.lcd_ann &= 0xF0; + mod_status.hdw.lcd_ann |= (int)data; + break; + + case 0x0C: /* LCD annunciator control (high nibble) */ + mod_status.hdw.lcd_ann &= 0x0F; + mod_status.hdw.lcd_ann |= ((int)data << 4); + break; + + case 0x0D: /* Serial port baud rate */ + break; + + case 0x0E: /* Card interface */ + case 0x0F: + break; + + case 0x10: /* Serial port interrupt and I/O control/status */ + Serial_IOC_Write(data); + break; + + case 0x11: /* Serial port receiver control/status register */ + Serial_RCS_Write(data); + break; + + case 0x12: /* Serial port transmitter control/status register */ + Serial_TCS_Write(data); + break; + + case 0x13: /* Clear serial port receive error */ + Serial_CRER_Write(data); + break; + + /* 3.13: A write operation into the receiver buffer register + triggers an extended emulator function. + */ + case 0x14: + ExtendedFunction(data); + break; + + /* Serial port transmitter buffer; the actual write takes place + when the MS nibble is written; serial_tbr buffers the LS nibble. + */ + case 0x16: + mod_status.hdw.serial_tbr = + (mod_status.hdw.serial_tbr & 0xF0) | (int8)data; + break; + + case 0x17: + mod_status.hdw.serial_tbr = + (mod_status.hdw.serial_tbr & 0x0F) | ((int8)data << 4); + Serial_TBR_Write(mod_status.hdw.serial_tbr); + break; + + case 0x18: /* Service request */ + case 0x19: + break; + + case 0x1A: /* IR Control Register */ + break; + + case 0x1B: /* Base nibble offset */ + break; + + case 0x1C: /* IR Status Register */ + break; + + case 0x1D: /* IR Led Buffer */ + break; + + case 0x1E: /* Scratch Pad */ + break; + + case 0x1F: /* Base Nibble */ + break; + + case 0x20: /* LCD base address register (write only) */ + case 0x21: + case 0x22: + case 0x23: + case 0x24: + mod_status.hdw.lcd_base_addr &= ~addr_mask[rel_address-0x20]; + mod_status.hdw.lcd_base_addr |= ((int)data << ((rel_address-0x20)*4)); + break; + + case 0x25: /* LCD line offset register */ + case 0x26: + case 0x27: + mod_status.hdw.lcd_line_offset &= ~addr_mask[rel_address-0x25]; + mod_status.hdw.lcd_line_offset |= ((int)data << ((rel_address-0x25)*4)); + break; + + case 0x28: /* LCD vertical line count (low nibble) */ + mod_status.hdw.lcd_vlc &= 0x30; + mod_status.hdw.lcd_vlc |= (int)data; + break; + + case 0x29: /* LCD vertical line count (higher 2 bits), others (TBD) */ + mod_status.hdw.lcd_vlc &= 0x0F; + mod_status.hdw.lcd_vlc |= (((int)data & 0x03) << 4); + + case 0x2E: /* Timer 1 Control */ + mod_status.hdw.t1_ctrl = data; + break; + + case 0x2F: /* Timer 2 Control */ + mod_status.hdw.t2_ctrl = data; + break; + + case 0x30: /* LCD menu address register (write only) */ + case 0x31: + case 0x32: + case 0x33: + case 0x34: + mod_status.hdw.lcd_menu_addr &= ~addr_mask[rel_address-0x30]; + mod_status.hdw.lcd_menu_addr |= ((int)data << ((rel_address-0x30)*4)); + break; + + case 0x37: /* Timer 1 value */ + mod_status.hdw.t1_val = data; + break; + + case 0x38: /* Timer 2 value */ + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + mod_status.hdw.t2_val &= ~int32_mask[rel_address-0x38]; + mod_status.hdw.t2_val |= ((int32)data << ((rel_address-0x38)*4)); + break; + + default: + ChfCondition MOD_W_HDW_WRITE, CHF_WARNING, rel_address, (int)data ChfEnd; + ChfSignal(); + } + + /* Save copy into hdw register array */ + mod_status.hdw.hdw[rel_address] = data; +} diff --git a/hw_config.c b/hw_config.c new file mode 100644 index 0000000..f5d3e14 --- /dev/null +++ b/hw_config.c @@ -0,0 +1,317 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: hw_config.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: hw_config.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 21-Sep-2000 +.keywords : * +.description : + This module contains the module description tables for all HPxx + hardware configurations currently supported. Moreover, it implements + the function ModSelectDescription(hw), to select and register a + module description table depending on an hardware configuration + selection string (hw). References: + + Guide to the Saturn Processor Rev. 1.0b by Matthew Mastracci + HP49 Memory Explained, USENET post, by Steve Sousa. + +.include : config.h machdep.h, cpu.h, modules.h + +.notes : + $Log: hw_config.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:43 cibrario + Added/Replaced GPL header + + Revision 3.3 2000/09/26 15:18:16 cibrario + Revised to implement Flash ROM write access: + - updated description of ModDescription table components + - the HP49 NCE3 controller now has MOD_MAP_FLAGS_ABS set in .map_flags + + * Revision 3.2 2000/09/22 14:40:18 cibrario + * *** empty log message *** + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: hw_config.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "debug.h" + +#define CHF_MODULE_ID MOD_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Module description tables + ---------------------------------------------------------------------------*/ + +extern void RomInit(void); +extern void HdwInit(void); +extern void RamInit(void); +extern void Ce1Init(void); +extern void Ce2Init(void); +extern void NCe3Init(void); + +extern void RomSave(void); +extern void HdwSave(void); +extern void RamSave(void); +extern void Ce1Save(void); +extern void Ce2Save(void); +extern void NCe3Save(void); + +extern Nibble RomRead(Address); +extern Nibble HdwRead(Address); +extern Nibble RamRead(Address); +extern Nibble Ce1Read(Address); +extern Nibble Ce2Read(Address); +extern Nibble NCe3Read(Address); + +extern void RomWrite(Address, Nibble); +extern void HdwWrite(Address, Nibble); +extern void RamWrite(Address, Nibble); +extern void Ce1Write(Address, Nibble); +extern void Ce2Write(Address, Nibble); +extern void NCe3Write(Address, Nibble); + +extern void RomInit49(void); +extern void HdwInit49(void); +extern void RamInit49(void); +extern void Ce1Init49(void); +extern void Ce2Init49(void); +extern void NCe3Init49(void); + +extern void RomSave49(void); +extern void HdwSave49(void); +extern void RamSave49(void); +extern void Ce1Save49(void); +extern void Ce2Save49(void); +extern void NCe3Save49(void); + +extern Nibble RomRead49(Address); +extern Nibble HdwRead49(Address); +extern Nibble RamRead49(Address); +extern Nibble Ce1Read49(Address); +extern Nibble Ce2Read49(Address); +extern Nibble NCe3Read49(Address); + +extern void RomWrite49(Address, Nibble); +extern void HdwWrite49(Address, Nibble); +extern void RamWrite49(Address, Nibble); +extern void Ce1Write49(Address, Nibble); +extern void Ce2Write49(Address, Nibble); +extern void NCe3Write49(Address, Nibble); + +static const struct +{ + const char *hw; + ModDescription description; +} + +table[] = + +{ + +/*--------------------------------------------------------------------------- + HP48 + ---------------------------------------------------------------------------*/ + + { + "hp48", + + { + /* name, id, access_prio, + init, save, + read, write, + r_config, r_abs_base_addr, r_size, + map_flags + */ + + { "ROM (ROM)", 0x00, 0, + RomInit, RomSave, + RomRead, RomWrite, + MOD_CONFIGURED, 0x00000, 0xFFFFF, + }, + + { "Hardware Regs. (HDW)", 0x19, 5, + HdwInit, HdwSave, + HdwRead, HdwWrite, + MOD_SIZE_CONFIGURED, 0x00000, 0x00040, + }, + + { "Internal RAM (RAM)", 0x03, 4, + RamInit, RamSave, + RamRead, RamWrite, + MOD_UNCONFIGURED, 0, 0, + }, + + { "Bank Select (CE1)", 0x05, 2, + Ce1Init, Ce1Save, + Ce1Read, Ce1Write, + MOD_UNCONFIGURED, 0, 0, + }, + + { "Port 1 Control (CE2)", 0x07, 3, + Ce2Init, Ce2Save, + Ce2Read, Ce2Write, + MOD_UNCONFIGURED, 0, 0, + }, + + { "Port 2 Control (NCE3)", 0x01, 1, + NCe3Init, NCe3Save, + NCe3Read, NCe3Write, + MOD_UNCONFIGURED, 0, 0, + } + }}, + +/*--------------------------------------------------------------------------- + HP49 + ---------------------------------------------------------------------------*/ + + { + "hp49", + + { + /* name, id, access_prio, + init, save, + read, write, + r_config, r_abs_base_addr, r_size, + map_flags + */ + + { "ROM (ROM)", 0x00, 0, + RomInit49, RomSave49, + RomRead49, RomWrite49, + MOD_CONFIGURED, 0x00000, 0xFFFFF, + }, + + { "Hardware Regs. (HDW)", 0x19, 5, + HdwInit, HdwSave, + HdwRead, HdwWrite, + MOD_SIZE_CONFIGURED, 0x00000, 0x00040, + }, + + { "IRAM (RAM)", 0x03, 4, + RamInit49, RamSave49, + RamRead49, RamWrite49, + MOD_UNCONFIGURED, 0, 0, + }, + + { "Bank Select (CE1)", 0x05, 2, + Ce1Init49, Ce1Save49, + Ce1Read49, Ce1Write49, + MOD_UNCONFIGURED, 0, 0, + }, + + { "ERAM Bank 0 (CE2)", 0x07, 3, + Ce2Init49, Ce2Save49, + Ce2Read49, Ce2Write49, + MOD_UNCONFIGURED, 0, 0, + }, + + { "ERAM Bank 1 (NCE3)", 0x01, 1, + NCe3Init49, NCe3Save49, + NCe3Read49, NCe3Write49, + MOD_UNCONFIGURED, 0, 0, + MOD_MAP_FLAGS_ABS + } + }} +}; + +#define N_DESCRIPTIONS (sizeof(table)/sizeof(table[0])) + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : ModSelectDescription +.kind : C function +.creation : 21-Sep-2000 +.description : + This function selects and registers (invoking ModRegisterDescription()) + a module description table depending on the hardware configuration + string passed as argument. + +.call : + ModSelectDescription(hw) +.input : + const char *hw, hardware configuration string +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_NO_MATCH +.notes : + 1.1, 28-Jan-1998, creation + +.- */ +void ModSelectDescription(const char *hw) +{ + int i; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "ModSelectDescription"); + + for(i=0; i +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "keyb.h" +#include "debug.h" + +#define CHF_MODULE_ID MOD_CHF_MODULE_ID +#include + +#define OUT_BITS 12 + +/* cur_in: + + This array contains the current value the CPU IN register will assume + for each bit set in the OUT register. +*/ +static InputRegister cur_in[OUT_BITS]; + + +/* .+ + +.title : KeybRSI +.kind : C function +.creation : 11-Feb-1998 +.description : + This function is called by the CPU emulator when the RSI instruction is + executed. It resets the keyboard interrupt system and posts a maskable + interrupt request if any key is pressed. + + NOTE: This function currently (r1.1) always posts an IRQ request; perhaps, + if the ON key is down, a NMI request should be posted instead. + +.call : + KeybRSI(); +.input : + void +.output : + void +.status_codes : + * +.notes : + 1.1, 17-Feb-1998, creation + +.- */ +void KeybRSI(void) +{ + /* Post an IRQ if the IN register is not zero */ + + CpuIntRequest(KeybIN((OutputRegister)0x1FF) != (InputRegister)0 ? + INT_REQUEST_IRQ : INT_REQUEST_NONE); +} + + +/* .+ + +.title : KeybIn +.kind : C function +.creation : 11-Feb-1998 +.description : + This function is called by the CPU emulator when either a C=IN or a A=IN + instruction is executed. It scans the keyboard and returns the current + value of the IN register for the given value of the OUT reguster. + +.call : + in = KeybIN(out); +.input : + OutputRegister out, current value of the OUT register +.output : + InputRegister in, computed value of the IN register +.status_codes : + * +.notes : + 1.1, 17-Feb-1998, creation + +.- */ +InputRegister KeybIN(OutputRegister out) +{ + /* Compute the current value of the IN register */ + InputRegister in = (InputRegister)0; + int bit; + + /* For each bit set in the 'out' register, OR the corresponding IN register + value into 'in' + */ + for(bit=0; bit>= 1; + } + + return in; +} + + +/* .+ + +.title : KeybPress +.kind : C function +.creation : 11-Feb-1998 +.description : + This function tells to the keyboard emulator that key 'key' has been + pressed. It updates the internal keyboard status information and, if + necessary, posts an interrupt request to the CPU. + +.call : + KeybPress(key); +.input : + const char *key, identifies the key that has been pressed. +.output : + void +.status_codes : + MOD_W_BAD_KEY + MOD_W_BAD_OUT_BIT +.notes : + 1.1, 17-Feb-1998, creation + 2.1, 6-Sep-2000, + deeply revised to accomodate the new GUI + +.- */ +void KeybPress(const char *key) +{ + if(strcmp(key, "*") == 0) + { + /* This is the ON key */ + int i; + + /* Set all 0x8000 lines */ + for(i=0; i= OUT_BITS) + { + ChfCondition MOD_W_BAD_OUT_BIT, CHF_WARNING, out_bit ChfEnd; + ChfSignal(); + } + + else + { + /* Update the cur_in array */ + cur_in[out_bit] |= in_val; + + /* Post an interrupt request to the CPU */ + CpuIntRequest(INT_REQUEST_NMI); + + } + } +} + + +/* .+ + +.title : KeybRelease +.kind : C function +.creation : 11-Feb-1998 +.description : + This function tells to the keyboard emulator that key 'key' has been + released. It updates the internal keyboard status information. + +.call : + KeybRelease(key); +.input : + const char *key, identifies the key that has been released. +.output : + void +.status_codes : + MOD_W_BAD_KEY + MOD_W_BAD_OUT_BIT +.notes : + 1.1, 17-Feb-1998, creation + 2.1, 6-Sep-2000, + deeply revised to accomodate the new GUI + +.- */ +void KeybRelease(const char *key) +{ + if(strcmp(key, "*") == 0) + { + /* This is the ON key */ + int i; + + /* Reset all 0x8000 lines */ + for(i=0; i= OUT_BITS) + { + ChfCondition MOD_W_BAD_OUT_BIT, CHF_WARNING, out_bit ChfEnd; + ChfSignal(); + } + + else + { + /* Update the cur_in array */ + cur_in[out_bit] &= ~in_val; + } + } +} + + +/* .+ + +.title : KeybReset +.kind : C function +.creation : 7-Nov-2000 +.description : + This function resets the emulated keyboard; all keys are released. + +.call : + KeybReset(); +.input : + void +.output : + void +.status_codes : +.notes : + 3.13, 7-Nov-2000, creation + +.- */ +void KeybReset(void) +{ + int i; + + /* Reset all 0x8000 lines */ + for(i=0; i BUILD_INFO.txt < +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "disk_io.h" +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID MOD_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Static/Global variables + ---------------------------------------------------------------------------*/ + +struct ModStatus mod_status; /* Status information - global */ + +/* 2.7: Replaced the statically-allocated module mapping structure with a + pointer to a dynamically-allocated structure, to be able to switch + between different structures fast. The mod_map macro can be used to + refer to the current module mapping structure, and mod_map_ptr + points to it. +*/ +static struct ModMap *mod_map_ptr; /* Module mapping information */ +#define mod_map (*mod_map_ptr) + +/* 2.7: All dynamically-allocated module mapping structures are linked + together; cache_head points to the head of the list. The list + is used to flush the cache when necessary. +*/ +static struct ModMap *cache_head = (struct ModMap *)NULL; + +/* 3.2: The ModDescription table is now configured invoking + ModRegisterDescription() before invoking any other function in this + module. +*/ +static const struct ModDescriptionEntry *mod_description; + + +/*--------------------------------------------------------------------------- + Debugging & performance analysis data + ---------------------------------------------------------------------------*/ + +#ifdef DEBUG + +static int alloc_c = 0; /* Counter of live AllocModMap() invocations */ +static int flush_c = 0; /* Counter of FlushCache() invocations */ +static int hit_c = 0; /* Cache hit counter */ +static int lhit_c = 0; /* Cache late unconfig hit counter */ +static int miss_c = 0; /* Cache miss (without replacement) counter */ +static int repl_c = 0; /* Entry replacement counter */ + +#define IncPerfCtr(x) x++ +#define DecPerfCtr(x) x-- +#define PrintPerfCtr(x) debug2(DEBUG_C_MOD_CACHE, MOD_I_PERF_CTR, #x, x) + +#define PrintCacheStats \ +{ \ + PrintPerfCtr(alloc_c); \ + PrintPerfCtr(flush_c); \ + PrintPerfCtr(hit_c); \ + PrintPerfCtr(lhit_c); \ + PrintPerfCtr(miss_c); \ + PrintPerfCtr(repl_c); \ +} + +#else + +#define IncPerfCtr(x) +#define DecPerfCtr(x) +#define PrintPerfCtr(x) + +#define PrintCacheStats + +#endif + + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : BadRead +.kind : C function +.creation : 26-Jan-1998 +.description : + This function is called when a read access is attempted for an unmapped + address. + + It signals a warning and returns 0x0 to the caller. Since two consecutive + zeros represent a RTNSXM instruction, this is an easy way to detect when + a gosub transfers control to an unmapped address. + +.call : + d = BadRead(addr); +.input : + Address addr, address +.output : + Nibble d, datum +.status_codes : + MOD_E_BAD_READ +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +static Nibble BadRead(Address addr) +{ + ChfCondition MOD_E_BAD_READ, CHF_ERROR, addr ChfEnd; + ChfSignal(); + + return (Nibble)0x0; +} + + +/* .+ + +.title : BadWrite +.kind : C function +.creation : 26-Jan-1998 +.description : + This function is called when a write access is attempted for an unmapped + address. + + It signals a warning and does not execute the write. + +.call : + BadWrite(addr, datum); +.input : + Address addr, address + Nibble datum, datum to be written +.output : + void +.status_codes : + MOD_E_BAD_WRITE +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +static void BadWrite(Address addr, Nibble datum) +{ + ChfCondition MOD_E_BAD_WRITE, CHF_ERROR, addr, datum ChfEnd; + ChfSignal(); +} + + +/* .+ + +.title : RebuildPageTable +.kind : C function +.creation : 26-Jan-1998 +.description : + This function rebuilds the module page table from page 'lo' to page 'hi', + inclusive, using the information contained in the current module + mapping structure (mod_map.map_info). + +.call : + RebuildPageTable(lo, hi); +.input : + int lo, first page table entry to rebuild + int hi, last page table entry to rebuild +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 26-Jan-1998, creation + 3.3, 25-Sep-2000, update + - implemented MOD_MAP_FLAGS_ABS flag in mod_description[].map_flags, + to allow module read/write functions to receive absolute addresses + instead of relative ones. + 3.5, 2-Oct-2000, update + - initialized 'winner' to an illegal value to track 'impossible' + module configurations. + +.- */ +static void RebuildPageTable(int lo, int hi) +{ + int page; + int mod; + + Address page_addr; + int prio; + int winner = -1; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RebuildPageTable"); + + /* Scan all pages in the [lo, hi] range */ + for(page=lo; page<=hi; page++) + { + /* Calculate the base page address for the current page */ + page_addr = ModAddress(page); + + /* Scan the module mapping information table, searching for the module + with the highest access priority that responds to the address + page_addr. If succesful, 'prio' contains the access priority of the + winner, and 'winner' contains its index, otherwise prio is set + to MOD_MIN_ACCESS_PRIO. + */ + prio = MOD_MIN_ACCESS_PRIO; + for(mod=0; mod= mod_map.map_info[mod].abs_base_addr && + page_addr < mod_map.map_info[mod].abs_base_addr + + mod_map.map_info[mod].size && + prio < mod_description[mod].access_prio + ) + { + winner = mod; + prio = mod_description[mod].access_prio; + } + } + + if(prio == MOD_MIN_ACCESS_PRIO) + { + /* The page is unmapped; set the module index to the special value + MOD_NO_MOD_INDEX, the relative base address to zero and the + read/write functions to BadRead/BadWrite, to catch accesses to + unmapped addresses. + */ + mod_map.page_table[page].index = MOD_NO_MOD_INDEX; + mod_map.page_table[page].rel_base_addr = 0x00000; + mod_map.page_table[page].read = BadRead; + mod_map.page_table[page].write = BadWrite; + } + + else + { + /* The page is mapped + 3.3: If the MOD_MAP_FLAGS_ABS is set in the winner's module + description, the base address of the page is set to its + absolute address; this way, the module read/write functions + will receive absolute addresses instead of relative ones. + */ + mod_map.page_table[page].index = winner; + mod_map.page_table[page].rel_base_addr = + (mod_description[winner].map_flags & MOD_MAP_FLAGS_ABS) + ? page_addr + : page_addr - mod_map.map_info[winner].abs_base_addr; + mod_map.page_table[page].read = mod_description[winner].read; + mod_map.page_table[page].write = mod_description[winner].write; + } + } +} + + +/* .+ + +.title : ClearCachingInfo +.kind : C function +.creation : 15-Sep-2000 +.description : + This function clears all caching information associated with the + struct ModMap pointed by its argument, and returns a pointer to + the same structure. + +.call : + d = ClearCachingInfo(d); +.input : + struct ModMap *d, ptr to the structure to be wiped off +.output : + struct ModMap *d, ptr to affected structure +.status_codes : + MOD_I_CALLED +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static struct ModMap *ClearCachingInfo(struct ModMap *d) +{ + static const struct ModCacheTableEntry empty = + { (Address)0, (struct ModMap *)NULL }; + + int i; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "ClearCachingInfo"); + + for(i=0; icache.config[i] = empty; + d->cache.victim = 0; + + for(i=0; icache.unconfig[i] = (struct ModMap *)NULL; + d->cache.config_point = 0; + d->cache.ref_count = 0; + + return d; +} + + +/* .+ + +.title : NewModMap +.kind : C function +.creation : 15-Sep-2000 +.description : + This function allocates a new struct ModMap, links it into the list of + cached ModMap , and returns a pointer to it; the function + signals a fatal condition if the allocation fails. + + Notice that this function does not initialize the struct ModMap in any + way; in particular, it does not clear the caching information. + + If DEBUG is appropriately enabled, this function prints out the + current value of all cache performance counters (PrintCacheStats). + +.call : + p = NewModMap(); +.input : + void +.output : + struct ModMap *p, pointer to the new struct ModMap +.status_codes : + MOD_I_CALLED + MOD_I_PERF_CTR, performance counter: %s value: %d + MOD_F_MAP_ALLOC, allocation of new map failed +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static struct ModMap *NewModMap(void) +{ + struct ModMap *new; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NewModMap"); + + if((new = (struct ModMap *)malloc(sizeof(struct ModMap))) + == (struct ModMap *)NULL) + { + ChfErrnoCondition; + ChfCondition MOD_F_MAP_ALLOC, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* Link new structure to the cache list */ + new->cache.link = cache_head; + cache_head = new; + + IncPerfCtr(alloc_c); + PrintCacheStats; + + return new; +} + + +/* .+ + +.title : CopyModMap +.kind : C function +.creation : 15-Sep-2000 +.description : + This function copies the contents of a struct ModMap into another, + and clears the caching information of the destination structure. + Returns a pointer to the destination structure. + + The linkage of the destination structure in the cached struct ModMap + list (.link field) is preserved. + +.call : + d = CopyModMap(d, s); +.input : + const struct ModMap *s, ptr to source structure +.output : + struct ModMap *d, ptr to destination structure +.status_codes : + MOD_I_CALLED +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static struct ModMap *CopyModMap(struct ModMap *d, const struct ModMap *s) +{ + struct ModMap *link = d->cache.link; /* Save .link of dest. */ + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "CopyModMap"); + + *d = *s; + d->cache.link = link; /* Restore .link */ + return ClearCachingInfo(d); +} + + +/* .+ + +.title : ReplaceModMap +.kind : C function +.creation : 15-Sep-2000 +.description : + This function replaces the struct ModMap pointed by *d with a copy of + the struct ModMap pointed by s; if *d is currently a NULL pointer, + a new struct ModMap is dynamically allocated before doing the copy + and a pointer to the new structure is stored into *d, else the + existing struct ModMap is overwritten. + + This function signals a fatal condition if the allocation of a new + struct ModMap is required, and fails. + + This function always clears the caching information in the + destination structure. + +.call : + ReplaceModMap(d, s); +.input : + struct ModMap **d, ptr to destination structure ptr + const struct ModMap *s, ptr to source structure +.output : + struct ModMap **d, ptr to destination structure ptr; + updated when original value of *d was NULL +.status_codes : + MOD_I_CALLED + MOD_I_PERF_CTR, performance counter: %s value: %d + MOD_F_MAP_ALLOC, allocation of new map failed +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static void ReplaceModMap(struct ModMap **d, const struct ModMap *s) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "ReplaceModMap"); + + if(*d == (struct ModMap *)NULL) + /* Allocation needed; cache cleared after allocation */ + *d = CopyModMap(NewModMap(), s); + + else + { + CopyModMap(*d, s); + IncPerfCtr(repl_c); + } +} + + +/* .+ + +.title : FlushCache +.kind : C function +.creation : 15-Sep-2000 +.description : + This function flushes the whole cache, freeing all structures previously + allocated by AllocModMap(), except that pointed by save. This function + also clears the caching information contained in save, because this + information is no longer valid after the flush. + +.call : + FlushCache(save); +.input : + struct ModMap *save +.output : + void +.status_codes : + MOD_I_CALLED + MOD_F_BAD_ALLOC_C, bad alloc_c (%d) after FlushCache() +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static void FlushCache(struct ModMap *save) +{ + struct ModMap *p, *n; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "FlushCache"); + + /* Scan the cache list; free all elements except that pointed by 'save' */ + p = cache_head; + while(p != (struct ModMap *)NULL) + { + + n = p->cache.link; + + if(p != save) + { + free(p); + DecPerfCtr(alloc_c); + } + + p = n; + } + + /* The cache list now contains only 'save' */ + save->cache.link = (struct ModMap *)NULL; + cache_head = save; + + /* Clear the caching information in 'save' */ + ClearCachingInfo(save); + + IncPerfCtr(flush_c); + +#ifdef DEBUG + /* The alloc_c performance counter must be exactly 1 now */ + if(alloc_c != 1) + { + ChfCondition MOD_F_BAD_ALLOC_C, CHF_ERROR, alloc_c ChfEnd; + ChfSignal(); + } +#endif +} + + +/* .+ + +.title : AccessConfigCache +.kind : C function +.creation : 15-Sep-2000 +.description : + This function checks if there is an entry in the module configuration cache + associated with the current struct ModMap with tag 'tag'; + if this is the case, it returns a pointer to the cached struct ModMap + just found, otherwise it returns NULL (cache miss). + +.call : + p = AccessConfigCache(tag); +.input : + Address tag, cache tag +.output : + struct ModMap *p, cached pointer, or NULL +.status_codes : + MOD_I_CALLED +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static struct ModMap *AccessConfigCache(Address tag) +{ + int i; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "AccessConfigCache"); + + for(i=0; icache.config_point) + p = p->cache.unconfig[i]; + + return p; +} + + +/* .+ + +.title : SelectConfigVictim +.kind : C function +.creation : 19-Sep-2000 +.description : + This function selects a victim entry in the module configuration + cache table of the current struct ModMap, updates the victim selection + info associated with the current map, and returns a pointer to the + victim entry. + + If the search fails and the 'retry' argument is non-zero, this + function signals a warning (MOD_W_NO_VICTIM), flushes the whole cache + (by means of FlushCache()), and retries the search. If even the + second attempt fails, it signals a fatal condition (MOD_F_NO_VICTIM). + + If the search fails and the 'retry' argument is zero, this + function immediately signals the fatal condition MOD_F_NO_VICTIM. + +.call : + victim = SelectConfigVictim(); +.input : + void +.output : + struct ModCacheTableEntry *victim, pointer to victim entry +.status_codes : + MOD_I_CALLED + MOD_W_NO_VICTIM + MOD_F_NO_VICTIM + MOD_F_BAD_ALLOC_C, bad alloc_c (%d) after FlushCache() +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +struct ModCacheTableEntry *SelectConfigVictim(int retry) +{ + int v = mod_map.cache.victim; + struct ModCacheTableEntry *victim = (struct ModCacheTableEntry *)NULL; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "AccessUnconfigCache"); + + /* Scan the config cache entries, starting at .cache.victim, + until a suitable one is found or the index loops around + */ + do + { + /* A config cache entry is suitable for use if: + - it is empty (map_ptr == NULL) + - or the reference count of the associated map is 0 + */ + if((mod_map.cache.config[v].map_ptr == (struct ModMap *)NULL) + || mod_map.cache.config[v].map_ptr->cache.ref_count == 0) + victim = &(mod_map.cache.config[v]); + + v = (v + 1) % N_MOD_CACHE_ENTRIES; + } + while(victim == (struct ModCacheTableEntry *)NULL + && v != mod_map.cache.victim); + + if(victim == (struct ModCacheTableEntry *)NULL) + { + if(retry) + { + /* Unable to find a victim; flush the cache and retry */ + ChfCondition MOD_W_NO_VICTIM, CHF_WARNING ChfEnd; + ChfSignal(); + + FlushCache(mod_map_ptr); + + victim = SelectConfigVictim(0); + } + + else + { + /* Unable to find a victim; retry is not an option; give up */ + ChfCondition MOD_F_NO_VICTIM, CHF_FATAL ChfEnd; + ChfSignal(); + } + } + + else + /* Found a victim; update next-victim index */ + mod_map.cache.victim = v; + + return victim; +} + + +/* .+ + +.title : CheckForLateHit +.kind : C function +.creation : 19-Sep-2000 +.description : + This function checks if there is in the cache a struct ModMap + containing the same module configuration information (.map_info field) + as the current one. + + If it founds a matching structure, this function returns a pointer + to it, otherwise it returns NULL. + + This function should be used after execution of an unconfig instruction + that encountered an early cache miss. + +.call : + p = CheckForLateHit(); +.input : + void +.output : + struct ModMap *p, cached pointer, or NULL +.status_codes : + MOD_I_CALLED +.notes : + 2.7, 19-Sep-2000, creation + +.- */ +static struct ModMap *CheckForLateHit(void) +{ + struct ModMap *p; + int i; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "AccessUnconfigCache"); + + p = cache_head; + + /* Scan the cache to find an entry with the same modules configuration + as the current one; return a pointer to it if successful + */ + while(p != (struct ModMap *)NULL) + { + /* Don't attempt to match an entry against itself */ + if(p != mod_map_ptr) + { + /* only .map_info contents must match */ + for(i=0; imap_info[i].config + != p->map_info[i].config) + || (mod_map_ptr->map_info[i].abs_base_addr != + p->map_info[i].abs_base_addr) + || (mod_map_ptr->map_info[i].size != + p->map_info[i].size)) + break; + } + + /* Break the while if we found a match ('for' was not broken) */ + if(i == N_MOD) break; + } + + /* Go to the next cache entry */ + p = p->cache.link; + } + + return p; +} + + +/* .+ + +.title : FreeModMap +.kind : C function +.creation : 19-Sep-2000 +.description : + This function frees the cached struct ModMap pointed by p, preserving + cache list linkage. + + It is responsibility of the caller to ensure that the structure is no + longer referenced. + +.call : + p = CheckForLateHit(); +.input : + void +.output : + struct ModMap *p, cached pointer, or NULL +.status_codes : + MOD_I_CALLED + MOD_F_CHAIN_CORRUPTED +.notes : + 2.7, 15-Sep-2000, creation + +.- */ +static void FreeModMap(struct ModMap *p) +{ + struct ModMap *n; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "FreeModMap"); + + /* Free the struct ModMap pointed by p, preserving the linkage of + other entries. The caller must ensure that the entry is not + referenced by any other entry through cache pointers. + */ + if(p == cache_head) + { + /* Free the list head */ + cache_head = p->cache.link; + free(p); + DecPerfCtr(alloc_c); + } + + else + { + /* Scan the cache; at end, n is either null (!) or points to the + cache entry that immediately precedes p + */ + n = cache_head; + while((n != (struct ModMap *)NULL) && n->cache.link != p) + n = n->cache.link; + + /* Should never happen */ + if(n == (struct ModMap *)NULL) + { + ChfCondition MOD_F_CHAIN_CORRUPTED, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* Bypass element pointed by p and free it */ + n->cache.link = p->cache.link; + free(p); + DecPerfCtr(alloc_c); + } +} + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : ModRegisterDescription +.kind : C function +.creation : 21-Sep-2000 +.description : + This function registers the ModDescription table pointed by 'p'; all + other module emulation functions will refer to that table in the future. + + It is mandatory to invoke this function with a valid ModDescription + table pointer as argument *before* using any other module emulation + function, either directly or indirectly. + + All error conditions are signalled using the Chf facility; the function + returns 'void' to the caller. + +.call : + ModRegisterDescription(p); +.input : + ModDescription p, module description table to be registered +.output : + void +.status_codes : + MOD_I_CALLED + +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void ModRegisterDescription(ModDescription p) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "ModRegisterDescription"); + mod_description = p; +} + + +/* .+ + +.title : ModInit +.kind : C function +.creation : 23-Jan-1998 +.description : + This function initializes all peripheral modules, calling its initialization + entry point; it must be called exactly once during startup. Then, it + initializes the modules mapping information either reading it from file or + resetting all modules. + + All error conditions are signalled using the Chf facility; the function + returns 'void' to the caller. + +.call : + ModInit(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_I_REVISION + MOD_I_INITIALIZING + MOD_I_PERF_CTR, performance counter: %s value: %d + MOD_W_RESETTING_ALL + MOD_F_MAP_ALLOC + MOD_F_NO_DESCRIPTION + + NOTE: This function can also (indirectly) report any condition + code generated and/or signalled by the module initialization + functions. +.notes : + 1.1, 23-Jan-1998, creation + 2.7, 15-Sep-2000, update + - revised to implement module config/unconfig cache + 3.2, 21-Sep-2000, update + - added sanity check on mod_description + +.- */ +void ModInit(void) +{ + int mod; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "ModInit"); + debug1(DEBUG_C_REVISION, MOD_I_REVISION, MOD_RCS_INFO); + + /* First, a little sanity check on mod_description: ensure that + ModRegisterDescription() has been called at least once with a + non-NULL argument. + */ + if(mod_description == NULL) + { + ChfCondition MOD_F_NO_DESCRIPTION, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* Scan the mod_description table, initializing all modules */ + for(mod=0; modtag = config_info; + ReplaceModMap(&(victim->map_ptr), mod_map_ptr); + + old = mod_map_ptr; + mod_map_ptr = victim->map_ptr; + + /* Scan the module information table searching for either an unconfigured + or a partially configured module + */ + for(mod=0; + modcache.ref_count++; + } +} + + +/* .+ + +.title : ModUnconfig +.kind : C function +.creation : 26-Jan-1998 +.description : + This function unconfigures the module currently configured at address + 'unconfig_info' and returns it to its after-reset configuration status. + + ModUnconfig also rebuilds the page table used for module access + to reflect the loss of visibility of the module in the CPU address space. + +.call : + ModUnconfig(unconfig_info); +.input : + Address unconfig_info, Unconfig information +.output : + void +.status_codes : + MOD_I_CALLED + MOD_I_UNCONFIG + MOD_I_CACHED_UNCONFIG + MOD_I_PERF_CTR, performance counter: %s value: %d + MOD_W_BAD_UNCONFIG + MOD_F_MAP_ALLOC + MOD_F_CHAIN_CORRUPTED +.notes : + 1.1, 26-Jan-1998, creation + 2.7, 15-Sep-2000, update + - implemented module config/unconfig cache + +.- */ +void ModUnconfig(Address unconfig_info) +{ + struct ModMap *nxt, *old; + int mod; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "ModUnconfig"); + + /* Determine the module to unconfigure */ + if((mod = mod_map.page_table[ModPage(unconfig_info)].index) == + MOD_NO_MOD_INDEX) + { + /* There isn't any module configured at the given address - + Signal a warning + */ + ChfCondition MOD_W_BAD_UNCONFIG, CHF_WARNING, unconfig_info ChfEnd; + ChfSignal(); + } + + else if(mod_description[mod].r_config == MOD_CONFIGURED) + { + /* The module is automatically configured after reset; it can never + be unconfigured. + */ + } + + else + { + /* Unconfiguring module 'mod': ACCESS UNCONFIG CACHE */ + if((nxt = AccessUnconfigCache(mod)) != (struct ModMap *)NULL) + { + /* CACHE HIT; switch mod_map_ptr */ + mod_map_ptr = nxt; + + IncPerfCtr(hit_c); + + debug0(DEBUG_C_MOD_CACHE, MOD_I_CACHED_UNCONFIG); + return; + } + + /* CACHE MISS + + A clone of the current struct ModMap is allocated and updated + according to the unconfig instruction being executed. + + Then, CheckForLateHit() is called to check whether in the + module mapping cache there is a struct ModMap identical to + the updated one. + + - If there is, the .unconfig[i] link is updated to point to + the cache entry just found. + + - If there is not, the whole cache is flushed and all cached + ModMap structures allocated so far are freed, except the + current one. I hope this occurrence is rare. + */ + + /* Save pointer to the old map and switch to a temporary one */ + old = mod_map_ptr; + mod_map_ptr = CopyModMap(NewModMap(), mod_map_ptr); + + /* Update the mapping information table */ + mod_map.map_info[mod].config = mod_description[mod].r_config; + + /* Rebuild the page table */ + RebuildPageTable( + ModPage(mod_map.map_info[mod].abs_base_addr), + ModPage(mod_map.map_info[mod].abs_base_addr + + mod_map.map_info[mod].size - 1)); + + /* Reset the module configuration status; the abs_base_addr of the module + is not reset because its old value is still needed by ModGetId() + The size is reset for the modules that are already MOD_SIZE_CONFIGURED + immediately after reset. + */ + mod_map.map_info[mod].size = mod_description[mod].r_size; + + if((nxt = CheckForLateHit()) != (struct ModMap *)NULL) + { + /* Update pointer from the old map to the new one, and increment + reference counter of the referenced structure + */ + old->cache.unconfig[mod] = nxt; + nxt->cache.ref_count++; + + /* Discard the temporary map and switch to the cached one */ + FreeModMap(mod_map_ptr); + mod_map_ptr = nxt; + + IncPerfCtr(lhit_c); + debug0(DEBUG_C_MOD_CACHE, MOD_I_UNCONFIG_L_HIT); + } + + else + { + /* Continue to use the new map with no caching information, + and hope that further configuration activities will link it + back in the immediate future. + */ + + /* Mark the current struct ModMap to be a configuration point; + this flag is used by the unconfig cache code to correctly + undo the last config + */ + mod_map.cache.config_point = 1; + + IncPerfCtr(miss_c); + + debug0(DEBUG_C_MOD_CACHE, MOD_I_UNCONFIG_L_MISS); + + debug3(DEBUG_C_MODULES|DEBUG_C_MOD_CACHE, MOD_I_UNCONFIG, + mod_description[mod].name, + mod_map.map_info[mod].abs_base_addr, + mod_map.map_info[mod].size + ); + } + } +} + + +/* .+ + +.title : FetchNibble +.kind : C function +.creation : 26-Jan-1998 +.description : + This function fetches a nibble from the address 'addr' and returns it. + + NOTE: This function DOES NOT update the hardware CRC register. + +.call : + d = FetchNibble(addr); +.input : + Address addr, address +.output : + Nibble *d, datum +.status_codes : + NOTE: This function indirectly reports and condition generated + and/or signalled by the module read function. +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +Nibble FetchNibble(Address addr) +{ + register int page = ModPage(addr); + + return mod_map.page_table[page].read( + mod_map.page_table[page].rel_base_addr | ModOffset(addr)); +} + + +/* .+ + +.title : ReadNibble +.kind : C function +.creation : 26-Jan-1998 +.description : + This function reads a nibble from the address 'addr' and returns it. + + NOTE: This function updates the hardware CRC register if the target of the + read operation is not the HDW module. The current (1.1) implementation + of this feature is inefficient because the .index field of the + addressed page must be checked against MOD_HDW_INDEX for each + access. + +.call : + d = ReadNibble(addr); +.input : + Address addr, address +.output : + Nibble *d, datum +.status_codes : + NOTE: This function indirectly reports and condition generated + and/or signalled by the module read function. +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +Nibble ReadNibble(Address addr) +{ + register int page = ModPage(addr); + register Nibble d; + + /* Read the nibble from the peripheral module */ + d = mod_map.page_table[page].read( + mod_map.page_table[page].rel_base_addr | ModOffset(addr)); + + /* Update the crc register, if appropriate */ + if(mod_map.page_table[page].index != MOD_HDW_INDEX) + mod_status.hdw.crc = + (mod_status.hdw.crc >> 4) ^ (((mod_status.hdw.crc ^ d) & 0x0F) * 0x1081); + + /* Return to the caller */ + return d; +} + + +/* .+ + +.title : WriteNibble +.kind : C function +.creation : 26-Jan-1998 +.description : + This function writes the nibble 'datum' to the address 'addr' + + NOTE: This function DOES NOT update the hardware CRC register. + +.call : + WriteNibble(addr, datum); +.input : + Address addr, destination address + Nibble datum, nibble to be written +.output : + void +.status_codes : + NOTE: This function indirectly reports and condition generated + and/or signalled by the module write function. +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +void WriteNibble(Address addr, Nibble datum) +{ + register int page = ModPage(addr); + + mod_map.page_table[page].write( + mod_map.page_table[page].rel_base_addr | ModOffset(addr), datum); +} + + +/*--------------------------------------------------------------------------- + Monitor functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : ModMapCheck +.kind : C function +.creation : 26-Jan-1998 +.description : + This function fills the string 'ob' with the current mapping information + for address 'addr'; it is used by the emulator monitor only. + +.call : + ModMapCheck(addr, char ob[MOD_MAP_CHECK_OB_SIZE]); +.input : + Address addr; +.output : + void +.status_codes : + * +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +void ModMapCheck(Address addr, char ob[MOD_MAP_CHECK_OB_SIZE]) +{ + int page; + Address offset; + int mod; + + page = ModPage(addr); + offset = ModOffset(addr); + + if((mod = mod_map.page_table[page].index) == MOD_NO_MOD_INDEX) + sprintf(ob, ChfGetMessage(CHF_MODULE_ID, MOD_M_NOT_MAPPED, ""), addr); + + else + { + Address rel_addr; + rel_addr = mod_map.page_table[page].rel_base_addr | offset; + + sprintf(ob, ChfGetMessage(CHF_MODULE_ID, MOD_M_MAPPED, ""), + addr, mod_description[mod].name, rel_addr); + } + + ChfSignal(); +} + + +/* .+ + +.title : ModMapTable +.kind : C function +.creation : 26-Jan-1998 +.description : + This function fills the string 'ob' with the current mapping table for + all modules; it is used by the emulator monitor only. + +.call : + ModMapTable(char ob[MOD_MAP_TABLE_OB_SIZE]); +.input : + Address addr; +.output : + void +.status_codes : + * +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +void ModMapTable(char ob[MOD_MAP_TABLE_OB_SIZE]) +{ + int mod; + + sprintf(ob, "%s\n", ChfGetMessage(CHF_MODULE_ID, MOD_M_MAP_TABLE_TITLE, "")); + ob += strlen(ob); + + for(mod=0; mod> 6)) +#define ModOffset(address) ((address) & 0x0003F) + + +/* + ModDescription + + This const array contains an entry for each peripheral module connected + to the peripheral bus of the Saturn CPU; the entry describes the + characteristics of the module. + + name: the mnemonic name of the module; the Saturn CPU doesn't + actually use this information, but it's still useful during + debugging. + + id: the ID of the module, returned by the C=ID instruction + when the module is unconfigured. + + access_prio: the access priority of the module, when the address spaces of + more than one module overlap. Higher values correspond to + higher priorities. + + The configuration priority of the module, when there is more + than one unconfigured module on the peripheral bus, is + determined implicitly by the order in which the module + descriptions into the array. The modules that come first + in the array are configured first. + + init: this function is called, without arguments, during VM startup + to initialize the device. For example, the initialization + function for the ROM module will read the ROM image from + disk and store them into the module status structure. + + read: this function reads a nibble from the module. It receives the + relative address of the nibble to be read. The read function + can return an interrupt request for the CPU. + + write: this function writes a nibble to the module. It receives the + relative address and the value of the nibble to be written. + The write function can return an interrupt request for the CPU. + + r_config: this flag contains the configuration status of the module after + a bus reset. If the after-reset configuration status is + MOD_CONFIGURED, the module can never be unconfigured. + + r_abs_base_addr: absolute base address of the module after a bus reset. + It should be set only if the module is at least partially + configured automatically after a bus reset. + + r_size: size of the address window of the module after a bus reset. + It should be set only if the module is at least partially + configured automatically after a bus reset. + + map_flags: special map flags: + MOD_MAP_FLAGS_ABS pass absolute addresses to module + read/write functions + + Notice that the current implementation requires that the index of + the HDW registers in the mod_description table must be fixed + and equal to MOD_HDW_INDEX... this is unfortunate. +*/ + +typedef void (*ModInitFunction)(void); + +typedef void (*ModSaveFunction)(void); + +typedef Nibble (*ModReadFunction)(Address rel_addr); + +typedef void (*ModWriteFunction)(Address rel_addr, Nibble data); + +enum ModConfig +{ + MOD_UNCONFIGURED, + MOD_SIZE_CONFIGURED, + MOD_CONFIGURED +}; + +struct ModDescriptionEntry +{ + char *name; + Address id; + int access_prio; +#define MOD_MIN_ACCESS_PRIO (-1) + + ModInitFunction init; + ModSaveFunction save; + ModReadFunction read; + ModWriteFunction write; + enum ModConfig r_config; + Address r_abs_base_addr; + Address r_size; + + int map_flags; /* 3.3 */ +#define MOD_MAP_FLAGS_ABS 0x1 /* Abs addresses to r/w */ +}; + +#define MOD_HDW_INDEX 1 +typedef const struct ModDescriptionEntry ModDescription[N_MOD]; + + +/* + ModMapInfo + + This array contains an entry for each peripheral module connected + to the peripheral bus of the Saturn CPU; the entry describes the + dynamic mapping information of the module: + + config: contains the current configuration status of the module. + + abs_base_addr: contains the current absolute base address of the module. + It's valid only if the module is currently configured. + + size: contains the current size of the address window of the module. + It's valid only if the module is currently configured. +*/ +struct ModMapInfoEntry +{ + enum ModConfig config; + Address abs_base_addr; + Address size; +}; + +typedef struct ModMapInfoEntry ModMapInfo[N_MOD]; + + +/* + ModPageTable + + This array contains an entry (of type ModPageTableEntry) for each 'page' + (of size #40 nibbles) of the Saturn CPU physical address space. For + each page, the following information is stored: + + index: the index of the module that responds to the address range of + the page in the ModDescription table.The special value + MOD_NO_MOD_INDEX indicates that no module responds to the + address range. + + rel_base_addr: the relative base address of the page in the address + space of the module that responds to the address range of + the page, if any. + + read, write: the read/write functions of the module that responds to the + address range of the page, if any. + + Relative address calculation for module access + + The Saturn Physical Address (SPA) is divided into two portions: + + * Page Index (PI): PI = (SPA & 0xFFFC0) >> 6 + * Offset (OFF): OFF = (SPA & 0x0003F) + + The Page Index determines which module will respond to the module + access operation. ModPageTable[PI] contains the following information: + + * Relative Base Address (RBA) + + The relative address (RA) therefore will be RA = RBA | OFF; then, the + appropriate module access function, found again in ModPageTable[PI], + is called. + +*/ +struct ModPageTableEntry +{ + int index; +#define MOD_NO_MOD_INDEX (-1) + + Address rel_base_addr; + ModReadFunction read; + ModWriteFunction write; +}; + +typedef struct ModPageTableEntry ModPageTable[N_PAGE_TABLE_ENTRIES]; + + +/* struct ModCache (2.7) + + This structure holds the caching information for module config/unconfig. + + The .config field is an array of ModCacheConfigEntry, and contains + the module configuration cache information. Each entry is a pair + (tag, map_ptr). The map_ptr field, when non-null, + points to the struct ModMap to be used when a module config command, + with the given tag address as argument, is executed. + + The .victim field points to the entry of .config to be used + whenever a new fresh cache entry is needed after a cache miss. + Currently, It is incremented by one at each replacement, thus + implementing a very simple fifo replacement policy. + + The .unconfig field is an array of struct ModMap pointers, and + contains the module unconfiguration cache information. + The .unconfig[i] array element, when non-null, points to the + struct ModMap to be used when a module unconfig command, unconfiguring + the i-th module, is executed. + + The .config_point field is set if the struct ModMap is a point + of the module configuration tree where a config was completed. + It it used to walk back correctly when caching an unconfig. + + This .ref_count is incremented by one when the struct ModMap + is referenced by an unconfig link; it is used to avoid freeing + referenced structures. + + The .link field links all cached struct ModMap together. +*/ +struct ModCacheTableEntry +{ + Address tag; + struct ModMap *map_ptr; +}; + +struct ModCache +{ + struct ModCacheTableEntry config[N_MOD_CACHE_ENTRIES]; + int victim; + + struct ModMap *(unconfig[N_MOD]); + + int config_point; + int ref_count; + + struct ModMap *link; +}; + + +/* + struct ModMap + + This structure contains all the mapping information about the peripheral + modules of the Saturn CPU. Its components are: + + map_info: this array describes the dynamic mapping information of + each module connected to the Saturn peripheral bus. + + page_table: this array describes the current layout of the address space + of the Saturn CPU. + + cache (2.7): this structure holds caching information used to speed up + module config/unconfig instructions + +*/ +struct ModMap +{ + ModMapInfo map_info; + ModPageTable page_table; + struct ModCache cache; +}; + + +/* + struct ModStatus + + This structure contains the actual status of all peripheral modules of the + Saturn CPU. The status of all modules is centralized to allow any device + to easily access the status of other devices. + + struct ModHdw + + This substructure contains the status of all peripheral devices controlled + by the hdw module. + + 3.2: To support the HP49 hw configuration, the original ModStatus structure + has been splitted in two: + + - (new) struct ModStatus: contains configuration-independent status + information (.hdw), and configuration-dependent accelerators (.hdw.accel). + The overall layout of the structure is the same for all configurations, + and it is publicily accessible. + + - struct ModStatus_xx (corresponding to all fields of the original + ModStatus structure except .hdw): contains the storage areas for + ROM and RAM in configuration 'xx', and is private to the + configuration-specific ROM/RAM emulation module. + +*/ +struct ModHdw48Accel +{ + XAddress bs_address; /* Bank Switcher ext. address */ +}; + +struct ModHdw49Accel +{ + XAddress view[2]; /* Base of Flash views */ +}; + +struct ModHdw +{ + Nibble hdw[N_HDW_SIZE]; /* HDW registers */ + + /* LCD driver */ + Address lcd_base_addr; /* LCD driver base address */ + int lcd_on; /* LCD driver enable flag */ + int lcd_contrast; /* LCD contrast value */ + int lcd_vlc; /* LCD vertical line count */ + int lcd_offset; /* LCD horizontal offset */ + int lcd_line_offset; /* LCD line offset */ + Address lcd_menu_addr; /* LCD menu base address */ + int lcd_ann; /* LCD annunciators status */ + + /* Timers */ + Nibble t1_ctrl; /* Timer 1 control */ +#define T1_CTRL_EXTRA 0x01 +#define T1_CTRL_INT 0x02 +#define T1_CTRL_WAKE 0x04 +#define T1_CTRL_SREQ 0x08 + + Nibble t2_ctrl; /* Timer 2 control */ +#define T2_CTRL_TRUN 0x01 +#define T2_CTRL_INT 0x02 +#define T2_CTRL_WAKE 0x04 +#define T2_CTRL_SREQ 0x08 + + Nibble t1_val; /* Timer 1 value */ + int32 t2_val; /* Timer 2 value */ + + /* 2.4: New member required to support Port emulation */ + Nibble card_status; /* Card status (hdw register 0x0F) */ +#define NCE3_CARD_PRESENT 0x01 +#define CE2_CARD_PRESENT 0x02 +#define NCE3_CARD_WE 0x04 +#define CE2_CARD_WE 0x08 + + /* 2.4: Hw configuration-specific members used as accelerators; + accel_valid is non-zero if the accelerators are valid + */ + int accel_valid; + + union + { + struct ModHdw48Accel a48; + struct ModHdw49Accel a49; + } accel; + + /* 2.5: Serial port buffer registers */ + int8 serial_rbr; + int8 serial_tbr; + + /* Misc */ + int16 crc; /* CRC */ +}; + +struct ModStatus +{ + struct ModHdw hdw; /* HDW status */ +}; + +struct ModStatus_48 +{ + Nibble rom[N_ROM_SIZE]; /* Internal ROM */ + Nibble ram[N_RAM_SIZE]; /* Internal RAM */ + Nibble port_1[N_PORT_1_SIZE]; /* 2.4: Port_1 (CE2) storage */ + + /* 2.4: Port_2 (NCE3) storage; only needed if N_PORT_2_BANK is defined */ +#ifdef N_PORT_2_BANK + Nibble port_2[N_PORT_2_SIZE]; +#endif +}; + +struct ModStatus_49 +{ + Nibble flash[N_FLASH_SIZE_49]; /* Internal Flash ROM */ + Nibble ram[N_RAM_SIZE_49]; /* Internal RAM */ + Nibble *ce2, *nce3; /* ERAM bases */ +}; + + +/*--------------------------------------------------------------------------- + Global variables + ---------------------------------------------------------------------------*/ + +extern struct ModStatus mod_status; + + +/*--------------------------------------------------------------------------- + Chf condition codes + ---------------------------------------------------------------------------*/ + +#define MOD_I_CALLED 101 /* Function %s called */ +#define MOD_I_INITIALIZING 102 /* Initializing module %s */ +#define MOD_I_RESETTING 103 /* Resetting module %s */ +#define MOD_I_GET_ID 106 /* ModGetID returning %x */ +#define MOD_I_CONFIG 107 /* ModConfig %s %x %x completed */ +#define MOD_I_UNCONFIG 108 /* ModUnconfig %s %x %x completed */ +#define MOD_I_SAVING 109 /* Saving status of module %s */ +#define MOD_I_NOT_IMPLEMENTED 110 /* Function %s not implemented */ +#define MOD_I_REVISION 111 /* Modules revision: %s */ +#define MOD_I_BS_ADDRESS 112 /* 2.4: Bank Switcher address: %x */ +#define MOD_I_PORT_1_WP 113 /* 2.4: Port 1 is write protected */ +#define MOD_I_PORT_2_WP 114 /* 2.4: Port 2 is write protected */ +#define MOD_I_PERF_CTR 115 /* 2.7: Value of PerfCtr %s is %d */ +#define MOD_I_CACHED_UNCONFIG 116 /* 2.7: Cached ModUnconfig completed */ +#define MOD_I_CACHED_CONFIG 117 /* 2.7: Cached ModConfig %x comp. */ +#define MOD_I_UNCONFIG_L_HIT 118 /* 2.7: Late unconfig hit */ +#define MOD_I_UNCONFIG_L_MISS 119 /* 2.7: Late unconfig miss */ +#define MOD_W_BAD_CONFIG 202 /* Bad ModConfig %x ignored */ +#define MOD_W_BAD_UNCONFIG 203 /* Bad ModUnconfig %x ignored */ +#define MOD_W_HDW_WRITE 204 /* Bad HdwWrite %x, %x */ +#define MOD_W_HDW_READ 205 /* Bad HdwRead %x */ +#define MOD_W_RESETTING_ALL 206 /* Resetting all modules */ +#define MOD_W_RAM_INIT 207 /* Can't initialize internal RAM */ +#define MOD_W_HDW_INIT 208 /* Can't initialize HDW */ +#define MOD_W_BAD_KEY 209 /* 2.1: Bad key %s ignored */ +#define MOD_W_BAD_OUT_BIT 210 /* 2.1: Bad out_bit %x ignored */ +#define MOD_W_PORT_1_INIT 211 /* 2.4: Can't initialize Port 1 */ +#define MOD_W_PORT_2_INIT 212 /* 2.4: Can't initialize Port 2 */ +#define MOD_W_NO_VICTIM 213 /* 2.7: No cache victim; flush/retry */ +#define MOD_E_BAD_READ 301 /* Read unmapped addr %x */ +#define MOD_E_BAD_WRITE 302 /* Write unmapped addr %x datum %x */ +#define MOD_E_ROM_WRITE 303 /* Write into ROM addr %x datum %x */ +#define MOD_E_RAM_SAVE 304 /* Can't save internal RAM status */ +#define MOD_E_HDW_SAVE 305 /* Can't save HDW status */ +#define MOD_E_PORT_1_SAVE 306 /* 2.4: Can't save Port 1 status */ +#define MOD_E_CE1_WRITE 307 /* 2.4: Ce1Write addr %x datum %x */ +#define MOD_E_PORT_2_SAVE 308 /* 2.4: Can't save Port 2 status */ +#define MOD_E_NCE3_READ 309 /* 2.4: Read from NCE3 addr %x */ +#define MOD_E_NCE3_WRITE 310 /* 2.4: Wr. to NCE3 addr %x datum %x */ +#define MOD_E_NO_MATCH 311 /* 3.2: Hw desription %s not found */ +#define MOD_E_ROM_SAVE 312 /* 3.3: Can't save Flash ROM */ +#define MOD_F_MAP_SAVE 401 /* Can't save mod_map information */ +#define MOD_F_ROM_INIT 402 /* Can't initialize internal ROM */ +#define MOD_F_MAP_ALLOC 403 /* Dynamic map allocation failed */ +#define MOD_F_BAD_ALLOC_C 404 /* 2.7: Bad alloc_c %d aft FlushCache*/ +#define MOD_F_CHAIN_CORRUPTED 405 /* 2.7: ModMap chain corrupted */ +#define MOD_F_NO_VICTIM 406 /* 2.7: No cache victim after flush */ +#define MOD_F_MOD_STATUS_ALLOC 407 /* 3.2: ModStatus_xx alloc failed %d */ +#define MOD_F_NO_DESCRIPTION 408 /* 3.2: No module description */ +#define MOD_M_NOT_MAPPED 501 /* Address %x not mapped */ +#define MOD_M_MAPPED 502 /* Address %x mapped to %s:%x */ +#define MOD_M_MAP_TABLE_TITLE 503 /* */ +#define MOD_M_MAP_TABLE_ROW 504 /* %s %x %x %s */ +#define MOD_M_MAP_CONFIGURED 505 /* Configured */ +#define MOD_M_MAP_SZ_CONFIGURED 506 /* Size configured */ +#define MOD_M_MAP_UNCONFIGURED 507 /* Unconfigured */ + + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/* Initialization */ +void ModSelectDescription(const char *hw); +void ModRegisterDescription(ModDescription p); +void ModInit(void); +void ModSave(void); +void ModReset(void); + +/* Configuration */ +Address ModGetID(void); +void ModConfig(Address config_info); +void ModUnconfig(Address unconfig_info); + +/* Read/Write */ +Nibble FetchNibble(Address addr); +Nibble ReadNibble(Address addr); +void WriteNibble(Address addr, Nibble datum); + +/* Monitor */ +void ModMapCheck(Address addr, char ob[MOD_MAP_CHECK_OB_SIZE]); +void ModMapTable(char ob[MOD_MAP_TABLE_OB_SIZE]); diff --git a/modules.msf b/modules.msf new file mode 100644 index 0000000..3d84032 --- /dev/null +++ b/modules.msf @@ -0,0 +1,103 @@ +$ .+ +$ .identifier : $Id: modules.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HP48 emulator +$ .title : $RCSfile: modules.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 13-Feb-1998 +$ .keywords : * +$ .description : +$ Message catalog source file for the peripheral modules emulator. +$ .notes : +$ . $Log: modules.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.8 2000/10/19 14:58:01 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 3.3 2000/09/26 15:05:33 cibrario +$ Revised to implement Flash ROM write access: +$ - Added message 312 +$ . +$ Revision 3.2 2000/09/22 14:07:04 cibrario +$ Implemented preliminary support of HP49 hw architecture: +$ - added new messages: 311, 407, 408 +$ . +$ Revision 2.7 2000/09/19 11:13:12 cibrario +$ Deeply revised to implement config/unconfig cache. +$ . +$ Revision 2.4 2000/09/12 15:48:57 cibrario +$ Added messages 112, 113, 114, 211, 212, 306, 307, 308, 309, 310 +$ . +$ Revision 2.1 2000/09/08 15:22:17 cibrario +$ Updated message 209; added message 210. Both updates reflect the +$ changes made to the keyboard emulation module in order to +$ accommodate the new GUI. +$ . +$ Revision 1.1 1998/02/17 14:53:53 cibrario +$ Initial revision +$ .- + +$set 1 +12 Modules + +$set 12 +101 Function [%s] called +102 Initializing module [%s] +103 Resetting module [%s] +106 ModGetID -> [%05X] +107 CONFIG M[%s] A[%05X] S[%05X] +108 UNCNFG M[%s] A[%05X] S[%05X] +109 Saving module [%s] +110 Function [%s] not implemented +111 Modules emulator: [%s] +112 Bank Switcher F/F set to A[%05X] +113 Port 1 has been write-protected +114 Port 2 has been write-protected +115 >>> PerfCtr [%s]=[%d] +116 Cached UNCNFG completed +117 Cached CONFIG A[%05X] completed +118 Late UNCNFG cache hit +119 Late UNCNFG cache miss +202 Bad ModConfig [%05X] ignored +203 Bad ModUnconfig [%05X] ignored +204 Bad HdwWrite A[%05X] N[%01X] +205 Bad HdwRead A[%05X] +206 Resetting all modules +207 Can't initialize internal RAM from disk +208 Can't initialize HDW from disk +209 Bad key [%s] ignored +210 Bad out_bit [%x] ignored +211 Can't initialize Port 1 from disk +212 Can't initialize Port 2 from disk +213 Unable to find CONFIG cache victim; flushing cache +301 Read from unmapped A[%05X] +302 Write to unmapped A[%05X] N[%01X] +303 Write into ROM A[%05X] D[%01X] +304 Can't save internal RAM status to disk +305 Can't save HDW status to disk +306 Can't save Port 1 status to disk +307 Write into CE1 A[%05X] D[%01X] +308 Can't save Port 1 status to disk +309 Read from NCE3 A[%05X] when Port 2 is not present +310 Write into NCE3 A[%05X] D[%01X] when Port 2 is not present +311 Hardware configuration [%s] not supported +312 Can't save Flash ROM status to disk +401 Can't save module mapping info +402 Can't initialize internal ROM +403 Dynamic allocation of ModMap failed +404 Bad alloc_c [%d] after FlushCache() +405 Cached struct ModMap chain corrupted; freeing unlinked entry +406 Unable to find CONFIG cache victim after cache flush +407 Allocation of ModStatus_xx failed ([%d]d bytes needed) +408 ModInit() invoked without registering a ModDescription first +501 A[%05X] -> *Not Mapped* +502 A[%05X] -> M[%s] R[%05X] +503 Device\t\t\tAddress\tSize\tStatus +504 %s\t%05X\t%05X\t%s +505 Configured +506 Size_configured +507 *Unconfigured* diff --git a/monitor.c b/monitor.c new file mode 100644 index 0000000..71de239 --- /dev/null +++ b/monitor.c @@ -0,0 +1,379 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: monitor.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: monitor.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 28-Jan-1998 +.keywords : * +.description : + This file implements a simple interactive monitor, useful during + test & debug. + +.include : config.h, machdep.h, cpu.h + +.notes : + $Log: monitor.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:51 cibrario + Added/Replaced GPL header + + Revision 3.1 2000/09/20 13:52:42 cibrario + Minor updates and fixes to avoid gcc compiler warnings on Solaris + when -ansi -pedantic -Wall options are selected. + + * Revision 1.1 1998/02/18 11:49:53 cibrario + * Initial revision + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: monitor.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID CPU_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Macro & Data type definitions + ---------------------------------------------------------------------------*/ + +#define LINE_BUFFER_SIZE 512 +#define TOK_DELIMITERS " \t\n" +#define ADDRESS_FMT "%x" +#define COUNT_FMT "%d" +#define PROMPT "> " +#define OK 0 +#define FAILED 1 + +/*--------------------------------------------------------------------------- + Private functions - Command line parse + ---------------------------------------------------------------------------*/ + +/* Read an Address from the command line */ +static int ReadHexAddress(Address *addr) +{ + char *p = strtok((char *)NULL, TOK_DELIMITERS); + return (p == (char *)NULL || + sscanf(p, ADDRESS_FMT, addr) != 1) ? FAILED : OK; +} + +/* Read a Nibble from the command line */ +static int ReadHexDatum(Nibble *n) +{ + Address addr; + int st; + if((st = ReadHexAddress(&addr)) == OK) *n = (Nibble)addr; + return st; +} + +/* Read a DECIMAL count from the command line */ +static int ReadCount(int *count) +{ + char *p = strtok((char *)NULL, TOK_DELIMITERS); + return (p == (char *)NULL || sscanf(p, COUNT_FMT, count) != 1 || + *count <= 0) ? FAILED : OK; +} + + +/*--------------------------------------------------------------------------- + Private functions - Command execution + ---------------------------------------------------------------------------*/ + +/* Run the emulator; this function exits normally only when an + EmulatorIntRequest() is posted and satisfied +*/ +static int run(void) +{ + Emulator(); + return OK; +} + +/* Set the debug level */ +static int debug(void) +{ + Address addr; + if(ReadHexAddress(&addr)) return FAILED; + SetDebugLevel((int)addr); + return OK; +} + +/* Check the mapping of an Address and print */ +static int map_check(void) +{ + Address addr; + char ob[MOD_MAP_CHECK_OB_SIZE]; + if(ReadHexAddress(&addr)) return FAILED; + ModMapCheck(addr, ob); + puts(ob); + return OK; +} + +/* Print the current module map table */ +static int map(void) +{ + char ob[MOD_MAP_TABLE_OB_SIZE]; + ModMapTable(ob); + puts(ob); + return OK; +} + +/* Write nibbles into memory */ +static int w(void) +{ + Address addr; + Nibble n; + if(ReadHexAddress(&addr)) return FAILED; + while(ReadHexDatum(&n) == OK) WriteNibble(addr++, n); + return OK; +} + +/* Read nibbles from memory */ +static int r(void) +{ + Address addr; + int count; + if(ReadHexAddress(&addr)) return FAILED; + if(ReadCount(&count)) count=1; + + while(count-->0) + { + printf("A_%05X\t%X\n", addr, (int)FetchNibble(addr)); + addr++; + } + + return OK; +} + +/* Disassemble */ +static int d(void) +{ + Address addr; + int count; + char ob[DISASSEMBLE_OB_SIZE]; + + if(ReadHexAddress(&addr)) return FAILED; + if(ReadCount(&count)) count=1; + + while(count-->0) + { + addr = Disassemble(addr, ob); + puts(ob); + } + + return OK; +} + +/* Print CPU status */ +static int cpu(void) +{ + char ob[DUMP_CPU_STATUS_OB_SIZE]; + DumpCpuStatus(ob); + puts(ob); + return OK; +} + +/* Reset CPU */ +static int reset(void) +{ + CpuReset(); + return OK; +} + +/* Save & Exit */ +static int mon_exit(void) +{ + ModSave(); + CpuSave(); + exit(EXIT_SUCCESS); + return OK; /* 3.1: Keep compiler happy */ +} + +/* Quit without saving */ +static int mon_quit(void) +{ + exit(EXIT_SUCCESS); + return OK; /* 3.1: Keep compiler happy */ +} + + +/*--------------------------------------------------------------------------- + Command table + ---------------------------------------------------------------------------*/ + +struct TEntry +{ + char *name; + char *desc; + int (*function)(void); +}; + +#define TableSize(t) (sizeof(t)/sizeof(struct TEntry)) + +/* Forward declaration for the Help funcion */ +static int Help(void); + +static const struct TEntry table[] = +{ + { "help", "Print this information", Help }, + { "run", "Run the emulator with current CPU status", run }, + { "?", ", Check address mapping", map_check }, + { "r", " [count], Read nibbles from memory", r }, + { "w", " [n]..., Write nibbles into memory", w }, + { "d", " [count], Disassemble starting from 'addr'", d }, + { "cpu", "Print CPU status", cpu }, + { "map", "Print the contents of the module map table", map }, + { "debug", "Set the debugging level", debug }, + { "reset", "Reset CPU", reset }, + { "exit", "Save emulator state & exit", mon_exit }, + { "quit", "Quit emulator WITHOUT saving its state", mon_quit } +}; + +/* Invoke the command 'tk' and return a status code */ +static int InvokeCommand(char *tk) +{ + int i; + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "disk_io.h" + +#define CHF_MODULE_ID UTIL_CHF_MODULE_ID +#include + + +/* Maximum size of source ROM (bytes) handled by this utility; set to + a reasonable value +*/ +#define MAX_SRC_SIZE (4*1024*1024) + + +/*--------------------------------------------------------------------------- + Chf parameters - Do not change. + The ABNORMAL_EXIT_CODE is taken from stdlib.h (EXIT_FAILURE) + ---------------------------------------------------------------------------*/ + +#define CONDITION_STACK_SIZE 16 +#define HANDLER_STACK_SIZE 8 +#define ABNORMAL_EXIT_CODE EXIT_FAILURE + +/* Conditional prefix and mandatory suffix to make a message catalog + name from cat_base_name. +*/ +static const char cat_prefix[] = "./"; +static const char cat_suffix[] = ".cat"; + +#define CAT_PREFIX_LEN (sizeof(cat_prefix)+1) +#define CAT_SUFFIX_LEN (sizeof(cat_suffix)+1) + +/* Message catalog base_name */ +static const char cat_base_name[] = "saturn"; + + +/* Condition codes used by this utility */ +#define UTIL_I_PACK_USAGE 1 +#define UTIL_F_PACK_CMD_LINE 2 +#define UTIL_F_PACK_STAT 3 +#define UTIL_F_PACK_SRC_SIZE 4 +#define UTIL_F_PACK_MALLOC 5 +#define UTIL_F_PACK_OPEN 6 +#define UTIL_F_PACK_READ 7 +#define UTIL_F_PACK_WRITE_NIBBLES 8 + + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : main +.kind : C function +.creation : 2-Oct-2000 +.description : + Main program. + +.notes : + 3.6, 2-Oct-2000, creation + +.- */ +int main(int argc, char *argv[]) +{ + char *cat_name; /* Message catalog name */ + struct stat statb; /* stat() buffer on source file */ + char *b; /* Source buffer */ + Nibble *nb; /* Nibble buffer */ + int d; /* Source file descriptor */ + int i; + int st; + + if((cat_name = malloc(sizeof(cat_base_name) + +CAT_PREFIX_LEN+CAT_SUFFIX_LEN+1)) + == NULL) + { + fprintf(stderr, "Cat_name initialization failed\n"); + exit(ABNORMAL_EXIT_CODE); + } + + /* Generate catalog name, without optional prefix */ + strcpy(cat_name, cat_base_name); + strcat(cat_name, cat_suffix); + + /* Chf initialization with msgcat subsystem; + notice that on some systems (e.g. Digital UNIX) catopen() can succeed + even if it was not able to open the right message catalog; better + try it now. + */ + if((st = ChfMsgcatInit( + argv[0], /* Application's name */ + CHF_DEFAULT, /* Options */ + cat_name, /* Name of the message catalog */ + CONDITION_STACK_SIZE, /* Size of the condition stack */ + HANDLER_STACK_SIZE, /* Size of the handler stack */ + ABNORMAL_EXIT_CODE /* Abnormal exit code */ + )) != CHF_S_OK + || + ChfGetMessage(CHF_MODULE_ID, UTIL_I_PACK_USAGE, NULL) == NULL) + { + if(st != CHF_S_OK && st != CHF_F_CATOPEN) + { + fprintf(stderr, "Chf initialization failed\n"); + exit(ABNORMAL_EXIT_CODE); + } + + else + { + fprintf(stderr, + "Default message catalog open failed; trying alternate\n"); + + /* Bring down Chf before initializing it again */ + if(st == CHF_S_OK) ChfExit(); + + /* Try alternate message catalog name (with prefix) */ + strcpy(cat_name, cat_prefix); + strcat(cat_name, cat_base_name); + strcat(cat_name, cat_suffix); + + if((st = ChfMsgcatInit( + argv[0], /* Application's name */ + CHF_DEFAULT, /* Options */ + cat_name, /* Name of the message catalog */ + CONDITION_STACK_SIZE, /* Size of the condition stack */ + HANDLER_STACK_SIZE, /* Size of the handler stack */ + ABNORMAL_EXIT_CODE /* Abnormal exit code */ + )) != CHF_S_OK + || + ChfGetMessage(CHF_MODULE_ID, UTIL_I_PACK_USAGE, NULL) == NULL) + { + fprintf(stderr, "Alternate Chf initialization failed\n"); + exit(ABNORMAL_EXIT_CODE); + } + } + } + + /* cat_name no longer needed */ + free(cat_name); + + /* Now, do some useful work; pack argv[1] into argv[2] */ + if(argc != 3) + { + ChfCondition UTIL_I_PACK_USAGE, CHF_INFO ChfEnd; + ChfCondition UTIL_F_PACK_CMD_LINE, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* Get the size of the source file */ + if(stat(argv[1], &statb)) + { + ChfErrnoCondition; + ChfCondition UTIL_F_PACK_STAT, CHF_FATAL, argv[1] ChfEnd; + ChfSignal(); + } + + /* Check that actual size is reasonable */ + if(statb.st_size > MAX_SRC_SIZE) + { + ChfCondition UTIL_F_PACK_SRC_SIZE, CHF_FATAL, statb.st_size ChfEnd; + ChfSignal(); + } + + /* Allocate source buffer */ + if((b = (char *)malloc(statb.st_size)) == (char *)NULL + || (nb = (Nibble *)malloc(sizeof(Nibble) * statb.st_size)) + == (Nibble *)NULL) + { + ChfErrnoCondition; + ChfCondition UTIL_F_PACK_MALLOC, CHF_FATAL, statb.st_size ChfEnd; + ChfSignal(); + + return EXIT_FAILURE; + } + + /* open/read/close */ + if((d = open(argv[1], O_RDONLY)) == -1) + { + ChfErrnoCondition; + ChfCondition UTIL_F_PACK_OPEN, CHF_FATAL, argv[1] ChfEnd; + ChfSignal(); + } + + if(read(d, b, statb.st_size) != statb.st_size) + { + ChfErrnoCondition; + + (void)close(d); + + ChfCondition UTIL_F_PACK_READ, CHF_FATAL, argv[1] ChfEnd; + ChfSignal(); + } + + (void)close(d); + + /* Convert char -> Nibble */ + for(i=0; i +#include +#include +#include +#include /* access() */ +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "disk_io.h" +#include "debug.h" + +#include "args.h" + +#define CHF_MODULE_ID MOD_CHF_MODULE_ID +#include + + +/* 3.2: The rom/ram storage areas are now dynamically allocated in + a private struct ModStatus_48. The dynamic allocation is performed during + Rom initialization, and the following macro allows us to reuse the + existing code with minimal updates. +*/ +static struct ModStatus_48 *mod_status_48; + +#define mod_status_hdw mod_status.hdw +#define mod_status_rom mod_status_48->rom +#define mod_status_ram mod_status_48->ram +#define mod_status_port_1 mod_status_48->port_1 +#define mod_status_port_2 mod_status_48->port_2 + + +/*--------------------------------------------------------------------------- + Rom module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : RomInit +.kind : C function +.creation : 23-Jan-1998 +.description : + This function allocates the dynamically-allocated portion of the + module status structure, and initializes the Rom module. + +.call : + RomInit(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_F_ROM_INIT + MOD_F_MOD_STATUS_ALLOC +.notes : + 1.1, 23-Jan-1998, creation + +.- */ +void RomInit(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomInit"); + + if((mod_status_48 = + (struct ModStatus_48 *)malloc(sizeof(struct ModStatus_48))) + == (struct ModStatus_48 *)NULL) + { + ChfErrnoCondition; + ChfCondition MOD_F_MOD_STATUS_ALLOC, CHF_FATAL, + sizeof(struct ModStatus_48) ChfEnd; + ChfSignal(); + } + + if(ReadNibblesFromFile(args.rom_file_name, N_ROM_SIZE, mod_status_rom)) + { + ChfCondition MOD_F_ROM_INIT, CHF_FATAL ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : RomSave +.kind : C function +.creation : 11-Feb-1998 +.description : + This function saves the status of the Rom module; actually it does + nothing. + +.call : + RomSave(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 11-Feb-1998, creation + +.- */ +void RomSave(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomSave"); +} + + +/* .+ + +.title : RomRead +.kind : C function +.creation : 26-Jan-1998 +.description : + This function reads a nibble from the internal ROM address 'rel_address' + and returns it. + +.call : + d = RomRead(rel_address); +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +Nibble RomRead(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomRead"); + + return mod_status_rom[rel_address]; +} + + +/* .+ + +.title : RomWrite +.kind : C function +.creation : 26-Jan-1998 +.description : + This function is called when the CPU attempt to write into an internal + ROM location. It signals an error condition and does nothing. + +.call : + RomWrite(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_ROM_WRITE +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +void RomWrite(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomWrite"); + + ChfCondition MOD_E_ROM_WRITE, CHF_ERROR, rel_address, datum ChfEnd; + ChfSignal(); +} + + +/*--------------------------------------------------------------------------- + Main Ram module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : RamInit +.kind : C function +.creation : 23-Jan-1998 +.description : + This function initializes the Ram module. + +.call : + RamInit(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_W_RAM_INIT +.notes : + 1.1, 23-Jan-1998, creation + +.- */ +void RamInit(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamInit"); + + if(ReadNibblesFromFile(args.ram_file_name, N_RAM_SIZE, mod_status_ram)) + { + ChfCondition MOD_W_RAM_INIT, CHF_WARNING ChfEnd; + ChfSignal(); + + (void)memset(mod_status_ram, 0, sizeof(mod_status_ram)); + } +} + + +/* .+ + +.title : RamSave +.kind : C function +.creation : 11-Feb-1998 +.description : + This function saves the status of the Ram module to disk. + +.call : + RamSave(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_RAM_SAVE +.notes : + 1.1, 11-Feb-1998, creation + 2.4, 12-Sep-2000, update + - upon failure, added push of ChfErrnoCondition to condition stack. + +.- */ +void RamSave(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamSave"); + + if(WriteNibblesToFile(mod_status_ram, N_RAM_SIZE, args.ram_file_name)) + { + ChfErrnoCondition; + ChfCondition MOD_E_RAM_SAVE, CHF_ERROR ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : RamRead +.kind : C function +.creation : 26-Jan-1998 +.description : + This function reads a nibble from the internal RAM address 'rel_address' + and returns it. + +.call : + d = RamRead(rel_address); +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +Nibble RamRead(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamRead"); + + return mod_status_ram[rel_address]; +} + + +/* .+ + +.title : RamWrite +.kind : C function +.creation : 26-Jan-1998 +.description : + This function writes the nibble 'datum' into the address 'rel_address' + of the internal RAM. + +.call : + RamWrite(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 26-Jan-1998, creation + +.- */ +void RamWrite(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamWrite"); + + mod_status_ram[rel_address] = datum; +} + + +/*--------------------------------------------------------------------------- + Ce1 module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : Ce1Init +.kind : C function +.creation : 23-Jan-1998 +.description : + This function initializes the Ce1 module, corresponding to the + Back Switcher. + +.call : + Ce1Init(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void Ce1Init(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Init"); + + /* Check if bank-switcher accelerators are valid; if not, initialize + them to a reasonable value (that is, select Port_2 bank 0). + */ + if(!mod_status_hdw.accel_valid) + { + mod_status_hdw.accel_valid = 1; + mod_status_hdw.accel.a48.bs_address = (XAddress)0; + } +} + + +/* .+ + +.title : Ce1Save +.kind : C function +.creation : 11-Feb-1998 +.description : + This function saves the status of the Ce1 module. + +.call : + Ce1Save(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 11-Feb-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void Ce1Save(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Save"); + + /* Nothing to be done herel the bank-switcher accelerators are saved + by the hdw modules + */ +} + + +/* .+ + +.title : Ce1Read +.kind : C function +.creation : 23-Jan-1998 +.description : + This function reads a nibble from the Ce1 module; the address of + the access cycle is converted into an XAddress and saved in + mod_status_hdw.accel.a48.bs_address. It will be used to supply the + most significant bits of Port_2 addresses when accessing that port. + +.call : + d = Ce1Read(rel_address); +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED + MOD_I_BS_ADDRESS +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +Nibble Ce1Read(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Read"); + debug1(DEBUG_C_MODULES, MOD_I_BS_ADDRESS, rel_address); + + /* Save the read address into the hdw accelerators. + bs_address can be directly or-ed with a relative port address to + obtain a valid index in Port_2 + */ +#ifdef N_PORT_2_BANK + mod_status_hdw.accel.a48.bs_address = + ((XAddress)((rel_address >> 1) & 0x1F) << 18) & (N_PORT_2_SIZE-1); +#endif + + return (Nibble)0x0; +} + + +/* .+ + +.title : Ce1Write +.kind : C function +.creation : 23-Jan-1998 +.description : + This function writes a nibble to the Ce1 module; the write attempt + is ignored and the status code MOD_E_CE1_WRITE is signaled. The + state of mod_status_hdw.accel.a48.bs_address is *not* changed. + +.call : + Ce1Write(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_CE1_WRITE +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void Ce1Write(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Write"); + + ChfCondition MOD_E_CE1_WRITE, CHF_ERROR, rel_address, datum ChfEnd; + ChfSignal(); +} + + +/*--------------------------------------------------------------------------- + Ce2 module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : Ce2Init +.kind : C function +.creation : 23-Jan-1998 +.description : + This function initializes the Ce2 module, corresponding to Port 1. + +.call : + Ce2Init(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_W_PORT_1_INIT + MOD_I_PORT_1_WP +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void Ce2Init(void) +{ + Nibble new_status; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Init"); + + if(ReadNibblesFromFile(args.port_1_file_name, N_PORT_1_SIZE, + mod_status_port_1)) + { + ChfCondition MOD_W_PORT_1_INIT, CHF_WARNING ChfEnd; + ChfSignal(); + + (void)memset(mod_status_port_1, 0, sizeof(mod_status_port_1)); + + new_status = + mod_status_hdw.card_status & ~(CE2_CARD_PRESENT|CE2_CARD_WE); + } + + else + { + /* Card present; check write protection */ + new_status = mod_status_hdw.card_status | CE2_CARD_PRESENT; + + if(access(args.port_1_file_name, W_OK) == 0) + new_status |= CE2_CARD_WE; + + else + { + new_status &= ~CE2_CARD_WE; + + ChfErrnoCondition; + ChfCondition MOD_I_PORT_1_WP, CHF_INFO ChfEnd; + ChfSignal(); + } + } + + if(new_status != mod_status_hdw.card_status) + { + /* card_status changed; update, set MP bit in HST and post + interrupt request. + */ + mod_status_hdw.card_status = new_status; + cpu_status.HST |= HST_MP_MASK; + CpuIntRequest(INT_REQUEST_IRQ); + } +} + + +/* .+ + +.title : Ce2Save +.kind : C function +.creation : 11-Feb-1998 +.description : + This function saves the status of the Ce2 module, if it is + not write-protected. + +.call : + Ce2Save(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_PORT_1_SAVE +.notes : + 1.1, 11-Feb-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void Ce2Save(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Save"); + + /* Attempt to save only if port is write-enabled */ + if((mod_status_hdw.card_status & CE2_CARD_WE) && + WriteNibblesToFile(mod_status_port_1, N_PORT_1_SIZE, + args.port_1_file_name)) + { + ChfErrnoCondition; + ChfCondition MOD_E_PORT_1_SAVE, CHF_ERROR ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : Ce2Read +.kind : C function +.creation : 23-Jan-1998 +.description : + This function reads a nibble from the Ce2 module. + +.call : + d = Ce2Read(rel_address) +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +Nibble Ce2Read(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Read"); + + return mod_status_port_1[rel_address]; +} + + +/* .+ + +.title : Ce2Write +.kind : C function +.creation : 23-Jan-1998 +.description : + This function writes a nibble to the Ce2 module. + +.call : + Ce2Write(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void Ce2Write(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Write"); + + mod_status_port_1[rel_address] = datum; +} + + +/*--------------------------------------------------------------------------- + NCe3 module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : NCe3Init +.kind : C function +.creation : 23-Jan-1998 +.description : + This function initializes the NCe3 module, corresponding to the + (bank switched) port 2. + +.call : + NCe3Init(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_W_PORT_2_INIT + MOD_I_PORT_2_WP +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void NCe3Init(void) +{ + Nibble new_status; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Init"); + +#ifdef N_PORT_2_BANK + if(ReadNibblesFromFile(args.port_2_file_name, N_PORT_2_SIZE, + mod_status_port_2)) + { + ChfCondition MOD_W_PORT_2_INIT, CHF_WARNING ChfEnd; + ChfSignal(); + + (void)memset(mod_status_port_2, 0, sizeof(mod_status_port_2)); + + new_status = + mod_status_hdw.card_status & ~(NCE3_CARD_PRESENT|NCE3_CARD_WE); + } + + else + { + /* Card present; check write protection */ + new_status = mod_status_hdw.card_status | NCE3_CARD_PRESENT; + + if(access(args.port_2_file_name, W_OK) == 0) + new_status |= NCE3_CARD_WE; + + else + { + new_status &= ~NCE3_CARD_WE; + + ChfErrnoCondition; + ChfCondition MOD_I_PORT_2_WP, CHF_INFO ChfEnd; + ChfSignal(); + } + } + +#else + /* If N_PORT_2_BANK is undefined, Port 2 is not emulated */ + new_status = + mod_status_hdw.card_status & ~(NCE3_CARD_PRESENT|NCE3_CARD_WE); + +#endif + + if(new_status != mod_status_hdw.card_status) + { + /* card_status changed; update, set MP bit in HST and post + interrupt request. + */ + mod_status_hdw.card_status = new_status; + cpu_status.HST |= HST_MP_MASK; + CpuIntRequest(INT_REQUEST_IRQ); + } +} + + +/* .+ + +.title : NCe3Save +.kind : C function +.creation : 11-Feb-1998 +.description : + This function saves the status of the NCe3 module. + +.call : + NCe3Save(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_PORT_2_SAVE +.notes : + 1.1, 11-Feb-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void NCe3Save(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Save"); + +#ifdef N_PORT_2_BANK + /* Attempt to save only if port is write-enabled */ + if((mod_status_hdw.card_status & NCE3_CARD_WE) && + WriteNibblesToFile(mod_status_port_2, N_PORT_2_SIZE, + args.port_2_file_name)) + { + ChfErrnoCondition; + ChfCondition MOD_E_PORT_2_SAVE, CHF_ERROR ChfEnd; + ChfSignal(); + } +#endif +} + + +/* .+ + +.title : NCe3Read +.kind : C function +.creation : 23-Jan-1998 +.description : + This function reads a nibble from the NCe3 module. + +.call : + d = NCe3Read(rel_address) +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED + MOD_E_NCE3_READ + +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +Nibble NCe3Read(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Read"); + +#ifdef N_PORT_2_BANK + return + mod_status_port_2[rel_address | mod_status_hdw.accel.a48.bs_address]; + +#else + ChfCondition MOD_E_NCE3_READ, CHF_ERROR, rel_address ChfEnd; + ChfSignal(); + return (Nibble)0; + +#endif +} + + +/* .+ + +.title : NCe3Write +.kind : C function +.creation : 23-Jan-1998 +.description : + This function writes a nibble to the NCe3 module; + it is not currently implemented. + +.call : + NCe3Write(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_NCE3_WRITE + +.notes : + 1.1, 23-Jan-1998, creation + 2.4, 11-Sep-2000, implemented + +.- */ +void NCe3Write(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Write"); + +#ifdef N_PORT_2_BANK + mod_status_port_2[rel_address | mod_status_hdw.accel.a48.bs_address] + = datum; + +#else + ChfCondition MOD_E_NCE3_WRITE, CHF_ERROR, rel_address, datum ChfEnd; + ChfSignal(); + +#endif +} diff --git a/romram49.c b/romram49.c new file mode 100644 index 0000000..404a66c --- /dev/null +++ b/romram49.c @@ -0,0 +1,887 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: romram49.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: romram49.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 21-Sep-2000 +.keywords : * +.description : + This module emulates the Internal Flash Rom/Ram peripheral modules of + the HP49. + + Known deficiencies of the current Flash ROM emulation: + + - for efficiency reasons, the Flash ROM state machine is bypassed + when the Flash is read through the ROM controller. + + - see flash49.h for additional, more specific information about + Flash ROM emulation issues. + + References: + + Guide to the Saturn Processor Rev. 1.0b by Matthew Mastracci + HP49 Memory Explained, USENET post, by Steve Sousa. + Emu48 Service Pack 20, by Christoph Gießelink. + 28F160S5/28F320S5 Data Sheet, by Intel Corp. + +.include : config.h, machdep.h, cpu.h, modules.h + +.notes : + $Log: romram49.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:14:55 cibrario + Added/Replaced GPL header + + Revision 3.4 2000/09/27 10:05:55 cibrario + Bug fix: MP bit of HST no longer set in Ce2Init49() and NCe3Init49(), + to avoid spurious warmstarts of the HP39/40 firmware. + + * Revision 3.3 2000/09/26 14:56:43 cibrario + * Revised to implement Flash ROM write access: + * - mod_status_49 is no longer static; flash49.c needs access to the + * Flash ROM array. + * - implemented RomSave49(), RomWrite49(). + * - NCe3 controller now accesses the Flash ROM when the LCR_LED bit is set; + * address translation is done here, the actual access is revectored to + * FlashRead49() and FlashWrite49(). Notice that the NCe3 controller + * must be registered with the MOD_MAP_FLAGS_ABS bit set in its map_flags. + * + * Revision 3.2 2000/09/22 14:48:13 cibrario + * *** empty log message *** + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: romram49.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include +#include /* access() */ +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "flash49.h" +#include "disk_io.h" +#include "debug.h" + +#include "args.h" + +#define CHF_MODULE_ID MOD_CHF_MODULE_ID +#include + + +#define FLASH_VIEW_SELECTOR 0x40000 +#define FLASH_BANK_MASK 0x3FFFF + +#define CE2_RAM_OFFSET 0x80000 +#define NCE3_RAM_OFFSET 0xC0000 +#define NCE3_RAM_MASK 0x3FFFF + +#define HDW_LCR_OFFSET 0x1C +#define LCR_LED 0x8 + +/* 3.3: This is no longer static, because flash49.c needs access to + the Flash ROM array... yes, I know this is not particularly nice, +*/ +struct ModStatus_49 *mod_status_49; + + +/*--------------------------------------------------------------------------- + Rom module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : RomInit49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function allocates the dynamically-allocated portion of the + module status structure, and initializes the Flash Rom module. + +.call : + RomInit49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_F_ROM_INIT + MOD_F_MOD_STATUS_ALLOC +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void RomInit49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomInit49"); + + if((mod_status_49 = + (struct ModStatus_49 *)malloc(sizeof(struct ModStatus_49))) + == (struct ModStatus_49 *)NULL) + { + ChfErrnoCondition; + ChfCondition MOD_F_MOD_STATUS_ALLOC, CHF_FATAL, + sizeof(struct ModStatus_49) ChfEnd; + ChfSignal(); + } + + if(ReadNibblesFromFile(args.rom_file_name, N_FLASH_SIZE_49, + mod_status_49->flash)) + { + ChfCondition MOD_F_ROM_INIT, CHF_FATAL ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : RomSave49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function saves the status of the Flash Rom. + +.call : + RomSave49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_ROM_SAVE +.notes : + 3.2, 21-Sep-2000, creation + 3.3, 25-Sep-2000, implemented + +.- */ +void RomSave49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomSave49"); + + if(WriteNibblesToFile(mod_status_49->flash, N_FLASH_SIZE_49, + args.rom_file_name)) + { + ChfErrnoCondition; + ChfCondition MOD_E_ROM_SAVE, CHF_ERROR ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : RomRead49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function reads a nibble from the internal Flash Rom address + 'rel_address' and returns it. + +.call : + d = RomRead49(rel_address); +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +Nibble RomRead49(Address rel_address) +{ + register XAddress view; + + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomRead49"); + + view = mod_status.hdw.accel.a49.view[ + (rel_address & FLASH_VIEW_SELECTOR) != 0]; + + return mod_status_49->flash[view | (rel_address & FLASH_BANK_MASK)]; +} + + +/* .+ + +.title : RomWrite49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function writes the nibble 'datum' into the address 'rel_address' + of the internal RAM. + + Flash ROM cannot be written using the ROM controller; however, + the current (1.19-4) HP49 firmware apparently executes ROM + write cycles through this controller when the ON key is pressed. + Those cycles are silently ignored. + +.call : + RomWrite49(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + 3.3, 26-Sep-2000, implemented + +.- */ +void RomWrite49(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomWrite49"); + + /* Ignore write cycles through ROM controller; HP49 ROM 1.19-4 + can do this when to ON key is pressed. + */ +} + + +/*--------------------------------------------------------------------------- + Main Ram module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : RamInit49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function initializes the Ram module. + +.call : + RamInit49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_W_RAM_INIT +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void RamInit49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamInit49"); + + if(ReadNibblesFromFile(args.ram_file_name, N_RAM_SIZE_49, + mod_status_49->ram)) + { + ChfCondition MOD_W_RAM_INIT, CHF_WARNING ChfEnd; + ChfSignal(); + + (void)memset(mod_status_49->ram, 0, sizeof(mod_status_49->ram)); + } +} + + +/* .+ + +.title : RamSave49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function saves the status of the Ram module to disk. + +.call : + RamSave49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED + MOD_E_RAM_SAVE +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void RamSave49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamSave49"); + + if(WriteNibblesToFile(mod_status_49->ram, N_RAM_SIZE_49, + args.ram_file_name)) + { + ChfErrnoCondition; + ChfCondition MOD_E_RAM_SAVE, CHF_ERROR ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : RamRead49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function reads a nibble from the internal RAM address 'rel_address' + and returns it. + +.call : + d = RamRead49(rel_address); +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +Nibble RamRead49(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamRead49"); + + return mod_status_49->ram[rel_address]; +} + + +/* .+ + +.title : RamWrite49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function writes the nibble 'datum' into the address 'rel_address' + of the internal RAM. + +.call : + RamWrite49(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void RamWrite49(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamWrite49"); + + mod_status_49->ram[rel_address] = datum; +} + + +/*--------------------------------------------------------------------------- + Ce1 module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : Ce1Init49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function initializes the Ce1 module, corresponding to the + Back Switcher. + +.call : + Ce1Init49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void Ce1Init49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Init49"); + + /* Check if bank-switcher accelerators are valid; if not, initialize + them to a reasonable value (that is, select Flash Rom bank 0 for + both views). + */ + if(!mod_status.hdw.accel_valid) + { + mod_status.hdw.accel_valid = 1; + mod_status.hdw.accel.a49.view[0] = + mod_status.hdw.accel.a49.view[1] = (XAddress)0; + } +} + + +/* .+ + +.title : Ce1Save49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function saves the status of the Ce1 module. + +.call : + Ce1Save49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void Ce1Save49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Save49"); + + /* Nothing to be done here; the bank-switcher accelerators are saved + by the hdw modules + */ +} + + +/* This fragment of code is used by both Ce1Read49() and Ce1Write49(); + the macro definition is here to allow us to write the code once and + use it many times without incurring a function call overhead. +*/ +#define Ce1SetViews \ +{ \ + mod_status.hdw.accel.a49.view[0] = \ + ((XAddress)((rel_address >> 5) & 0x03) << 18); \ + \ + mod_status.hdw.accel.a49.view[1] = \ + ((XAddress)((rel_address >> 1) & 0x0F) << 18); \ +} + + +/* .+ + +.title : Ce1Read49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function reads a nibble from the Ce1 module; the address of + the access cycle is converted into a pair of XAddress + (base addresses of Flash Rom views) and saved into + mod_status_hdw.accel.a49.view[]. They will be used to supply the + most significant bits of addresses when accessing Flash Rom. + +.call : + d = Ce1Read49(rel_address); +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED + MOD_I_BS_ADDRESS +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +Nibble Ce1Read49(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Read49"); + debug1(DEBUG_C_MODULES, MOD_I_BS_ADDRESS, rel_address); + + /* Save the ROM view base addresses address into the hdw accelerators. + view[] can be directly or-ed with a relative port address to + obtain a valid index in Flash Rom. + */ + Ce1SetViews; + + return (Nibble)0x0; +} + + +/* .+ + +.title : Ce1Write49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function reads a nibble from the Ce1 module; the address of + the access cycle is converted into a pair of XAddress + (base addresses of Flash Rom views) and saved into + mod_status_hdw.accel.a49.view[]. They will be used to supply the + most significant bits of addresses when accessing Flash Rom. + +.call : + Ce1Write49(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory; ignored +.output : + void +.status_codes : + MOD_I_CALLED + MOD_I_BS_ADDRESS +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void Ce1Write49(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Write49"); + debug1(DEBUG_C_MODULES, MOD_I_BS_ADDRESS, rel_address); + + /* Save the ROM view base addresses address into the hdw accelerators. + view[] can be directly or-ed with a relative port address to + obtain a valid index in Flash Rom. + */ + Ce1SetViews; +} + + +/*--------------------------------------------------------------------------- + Ce2 module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : Ce2Init49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function initializes the Ce2 module, corresponding to + the first bank of ERAM. + +.call : + Ce2Init49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + 3.4, 27-Sep-2000, update: + - MP bit in HST no longer set, to avoid spurious warmstarts of the + HP39/40 firmware. + +.- */ +void Ce2Init49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Init49"); + + /* Set base of ce2 area */ + mod_status_49->ce2 = mod_status_49->ram + CE2_RAM_OFFSET; + + /* CE2 always present and write enabled */ + mod_status.hdw.card_status |= (CE2_CARD_PRESENT|CE2_CARD_WE); + +#if 0 + /* card_status changed; update, set MP bit in HST and post + interrupt request. + */ + cpu_status.HST |= HST_MP_MASK; + CpuIntRequest(INT_REQUEST_IRQ); +#endif +} + + +/* .+ + +.title : Ce2Save49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function saves the status of the Ce2 module. + +.call : + Ce2Save49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void Ce2Save49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Save49"); + + /* Do nothing; the whole RAM is saved by RamSave49() */ +} + + +/* .+ + +.title : Ce2Read49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function reads a nibble from the Ce2 module. + +.call : + d = Ce2Read49(rel_address) +.input : + Address rel_address, memory address +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +Nibble Ce2Read49(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Read49"); + + return mod_status_49->ce2[rel_address]; +} + + +/* .+ + +.title : Ce2Write49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function writes a nibble to the Ce2 module. + +.call : + Ce2Write49(rel_address, datum); +.input : + Address rel_address, memory address + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void Ce2Write49(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Write49"); + + mod_status_49->ce2[rel_address] = datum; +} + + +/*--------------------------------------------------------------------------- + NCe3 module + ---------------------------------------------------------------------------*/ + +/* .+ + +.title : NCe3Init49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function initializes the Ce2 module, corresponding to + the first bank of ERAM. + +.call : + NCe3Init49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + 3.4, 27-Sep-2000, update: + - MP bit in HST no longer set, to avoid spurious warmstarts of the + HP39/40 firmware. + +.- */ +void NCe3Init49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Init49"); + + /* Set base of nce3 area */ + mod_status_49->nce3 = mod_status_49->ram + NCE3_RAM_OFFSET; + + /* NCE3 always present and write enabled */ + mod_status.hdw.card_status |= (NCE3_CARD_PRESENT|NCE3_CARD_WE); + +#if 0 + /* card_status changed; update, set MP bit in HST and post + interrupt request. + */ + cpu_status.HST |= HST_MP_MASK; + CpuIntRequest(INT_REQUEST_IRQ); +#endif +} + + +/* .+ + +.title : NCe3Save49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function saves the status of the NCe3 module. + +.call : + NCe3Save49(); +.input : + void +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + +.- */ +void NCe3Save49(void) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Save49"); + + /* Do nothing; the whole RAM is saved by RamSave49() */ +} + + +/* .+ + +.title : NCe3Read49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function reads a nibble from the NCe3 module. + If LCR_LED bit is not set, data comes from ERAM Bank 1, if LCR_LED is set, + data comes from the Flash ROM. + + In both cases, address translation is done here. + + This module must have the MOD_MAP_FLAGS_ABS set in + its mod_description[].map_flags; this way, rel_address will be + the ABSOLUTE address of the memory access. + +.call : + d = NCe3Read49(rel_address) +.input : + Address rel_address, memory address (ABSOLUTE) +.output : + Nibble *d, datum read from memory +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + 3.3, 25-Sep-2000, update + - added vectors into flash49 for FlashROM read ops + +.- */ +Nibble NCe3Read49(Address rel_address) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Read49"); + + return + (mod_status.hdw.hdw[HDW_LCR_OFFSET] & LCR_LED) + ? FlashRead49( + mod_status.hdw.accel.a49.view[ + (rel_address & FLASH_VIEW_SELECTOR) != 0] + | (rel_address & FLASH_BANK_MASK)) + : mod_status_49->nce3[rel_address & NCE3_RAM_MASK]; +} + + +/* .+ + +.title : NCe3Write49 +.kind : C function +.creation : 21-Sep-2000 +.description : + This function writes a nibble to the NCe3 module. + If LCR_LED bit is not set, data goes to ERAM Bank 1, if LCR_LED is set, + data goes to the Flash ROM. + + In both cases, address translation is done here. + + This module must have the MOD_MAP_FLAGS_ABS set in + its mod_description[].map_flags; this way, rel_address will be + the ABSOLUTE address of the memory access. + + +.call : + NCe3Write49(rel_address, datum); +.input : + Address rel_address, memory address (ABSOLUTE) + Nibble datum, datum to be written into memory +.output : + void +.status_codes : + MOD_I_CALLED +.notes : + 3.2, 21-Sep-2000, creation + 3.3, 25-Sep-2000, update + - added vectors into flash49 for FlashROM write ops + +.- */ +void NCe3Write49(Address rel_address, Nibble datum) +{ + debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Write49"); + + if(mod_status.hdw.hdw[HDW_LCR_OFFSET] & LCR_LED) + FlashWrite49(mod_status.hdw.accel.a49.view[ + (rel_address & FLASH_VIEW_SELECTOR) != 0] + | (rel_address & FLASH_BANK_MASK), + datum); + else + mod_status_49->nce3[rel_address & NCE3_RAM_MASK] = datum; +} diff --git a/run_saturn b/run_saturn new file mode 100755 index 0000000..d89e0cb --- /dev/null +++ b/run_saturn @@ -0,0 +1,18 @@ +#!/bin/sh +# +# $Id: run_saturn,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +# +# This file allows you to run saturn directly from the build directory, +# without installing it; regular use is discouraged. + +if [ ! -r Saturn ]; then ln -s Saturn.ad Saturn; fi + +# Be sure that XUSERFILESEARCHPATH does not supersede XAPPLRESDIR (IRIX) +if [ "$XUSERFILESEARCHPATH" != "" ]; then + XUSERFILESEARCHPATH=./%L/%N:./%l/%N:./%N:$XUSERFILESEARCHPATH; + export XUSERFILESEARCHPATH; +fi + +XAPPLRESDIR=.; export XAPPLRESDIR # Take resources from here +NLSPATH=./%N; export NLSPATH # Message catalog is here, too +./saturn $* diff --git a/saturn.c b/saturn.c new file mode 100644 index 0000000..3e0f9f0 --- /dev/null +++ b/saturn.c @@ -0,0 +1,310 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: saturn.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: saturn.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 8-Sep-2000 +.keywords : * +.description : + This file contains the main program of the Saturn CPU / HP4x emulator + +.include : * + +.notes : + $Log: saturn.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.15 2000/11/15 14:07:53 cibrario + GUI enhancements and assorted bug fixes: + + - made Chf initialization more robust with respect to incorrect + locale settings + + - made stand-alone messages conforming to Chf standard format + + - the copyright notice is now output through the GUI, too, if stdout + is not a tty + + Revision 3.14 2000/11/13 11:31:16 cibrario + Implemented fast load/save; improved keyboard interface emulation at + high emulated CPU speed: + + - Revision number bump with no changes + + Revision 3.11 2000/10/25 11:14:35 cibrario + *** empty log message *** + + Revision 3.10 2000/10/24 16:14:56 cibrario + Added/Replaced GPL header + + Revision 3.9 2000/10/24 16:11:20 cibrario + The main program now has its own set of condition codes; + added printf of MAIN_M_COPYRIGHT and MAIN_M_LICENSE as suggested by GPL + + Revision 3.8 2000/10/23 13:16:49 cibrario + Bug fix: + Improper use of sizeof() in main() could give a segmentation fault. + + Revision 3.1 2000/09/20 13:58:41 cibrario + Minor updates and fixes to avoid gcc compiler warnings on Solaris + when -ansi -pedantic -Wall options are selected. + + * Revision 2.5 2000/09/14 15:32:07 cibrario + * Added invotation of SerialInit() in main program, to initialize + * the serial port emulation module. + * + * Revision 2.1 2000/09/08 15:46:02 cibrario + * *** empty log message *** + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: saturn.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include /* 3.1: strcpy(), strcat() */ +#include /* isatty() */ + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "x11.h" +#include "serial.h" +#include "args.h" +#include "debug.h" + + +/* Chf condition codes (main program only) */ + +#define CHF_MODULE_ID MAIN_CHF_MODULE_ID +#include + +#define MAIN_M_COPYRIGHT 501 +#define MAIN_M_LICENSE 502 + + +/*--------------------------------------------------------------------------- + Chf parameters - Do not change. + The ABNORMAL_EXIT_CODE is taken from stdlib.h (EXIT_FAILURE) + ---------------------------------------------------------------------------*/ + +#define CONDITION_STACK_SIZE 16 +#define HANDLER_STACK_SIZE 8 +#define ABNORMAL_EXIT_CODE EXIT_FAILURE + +/* Conditional prefix and mandatory suffix to make a message catalog + name from argv[0] +*/ +static const char cat_prefix[] = "./"; +static const char cat_suffix[] = ".cat"; + +#define CAT_PREFIX_LEN (sizeof(cat_prefix)+1) +#define CAT_SUFFIX_LEN (sizeof(cat_suffix)+1) + + +static void adjust_setlocale(void) +{ + fprintf(stderr, + "saturn-W-locale probably bad; reverting to C locale\n"); + + putenv("LC_ALL=C"); + putenv("LC_COLLATE=C"); + putenv("LC_CTYPE=C"); + putenv("LC_MESSAGES=C"); + putenv("LC_MONETARY=C"); + putenv("LC_NUMERIC=C"); + putenv("LC_TIME=C"); + putenv("LANG=C"); +} + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : main +.kind : C function +.creation : 8-Sep-2000 +.description : + Main program. + +.notes : + 2.1, 6-Sep-2000, creation + 2.5, 14-Sep-2000, update + - added invotation of SerialInit(). + 3.9, 23-Oct-2000, update + - main() now has its own set of condition codes + 3.15, 15-Nov-2000, update + - made Chf initialization more robust with respect to bad locales + +.- */ +int main(int argc, char *argv[]) +{ + char *cat_name; + int st; + int retry = 0; + + if((cat_name = malloc(strlen(argv[0])+CAT_PREFIX_LEN+CAT_SUFFIX_LEN+1)) + == NULL) + { + fprintf(stderr, "saturn-E-cat_name initialization failed\n"); + exit(ABNORMAL_EXIT_CODE); + } + + /* Generate catalog name, without optional prefix */ + strcpy(cat_name, argv[0]); + strcat(cat_name, cat_suffix); + + /* 3.15: Retry the initialization steps below two times; before trying + the second time, adjust the setlocale() environment variables + with adjust_setlocale() + */ + while(retry < 2) + { + /* Chf initialization with msgcat subsystem; notice that on + some systems (e.g. Digital UNIX) catopen() can succeed even + if it was not able to open the right message catalog; + better try it now. + */ + if((st = ChfMsgcatInit( + argv[0], /* Application's name */ + CHF_DEFAULT, /* Options */ + cat_name, /* Name of the message catalog */ + CONDITION_STACK_SIZE, /* Size of the condition stack */ + HANDLER_STACK_SIZE, /* Size of the handler stack */ + ABNORMAL_EXIT_CODE /* Abnormal exit code */ + )) != CHF_S_OK + || + ChfGetMessage(CHF_MODULE_ID, MAIN_M_COPYRIGHT, NULL) == NULL) + fprintf(stderr, + "saturn-E-Primary Chf initialization failed (%d)\n", st); + + else + break; + + /* Bring down Chf before initializing it again */ + if(st == CHF_S_OK) ChfExit(); + + /* Try alternate message catalog name (with prefix) */ + strcpy(cat_name, cat_prefix); + strcat(cat_name, argv[0]); + strcat(cat_name, cat_suffix); + + if((st = ChfMsgcatInit( + argv[0], /* Application's name */ + CHF_DEFAULT, /* Options */ + cat_name, /* Name of the message catalog */ + CONDITION_STACK_SIZE, /* Size of the condition stack */ + HANDLER_STACK_SIZE, /* Size of the handler stack */ + ABNORMAL_EXIT_CODE /* Abnormal exit code */ + )) != CHF_S_OK + || + ChfGetMessage(CHF_MODULE_ID, MAIN_M_COPYRIGHT, NULL) == NULL) + fprintf(stderr, + "saturn-E-Alternate Chf initialization failed (%d)\n", + st); + + else + break; + + /* Bring down Chf before initializing it again */ + if(st == CHF_S_OK) ChfExit(); + + /* Attempt to adjust setlocale() environment variables */ + if(retry++ == 0) adjust_setlocale(); + } + + if(retry == 2) + { + fprintf(stderr, "saturn-F-Application aborted\n"); + exit(ABNORMAL_EXIT_CODE); + } + + /* cat_name no longer needed */ + free(cat_name); + + /* 3.9: Print out MAIN_M_COPYRIGHT and MAIN_M_LICENSE on stdout now */ + fprintf(stdout, ChfGetMessage(CHF_MODULE_ID, MAIN_M_COPYRIGHT, ""), + "$Revision: 4.1 $"); + fprintf(stdout, ChfGetMessage(CHF_MODULE_ID, MAIN_M_LICENSE, "")); + + /* Initialize GUI and associated lcd display emulation module */ + InitializeGui(argc, argv); + + /* Initialize serial port emulation. + This function returns the name of the slave side of the pty; + it is unused here, because the GUI condition handler intercepts + a condition containing the same information and displays the pty name + on the main emulator's window. + */ + (void)SerialInit(); + + /* Initialize emulator proper */ + EmulatorInit(); + + /* 3.15: Repeat copyright message on GUI if stdout is not a tty */ + if(! isatty(fileno(stdout))) + { + ChfCondition MAIN_M_LICENSE, CHF_INFO ChfEnd; + ChfCondition MAIN_M_COPYRIGHT, CHF_INFO, "$Revision: 4.1 $" ChfEnd; + + ChfSignal(); + } + + if(args.monitor) + { + /* Invoke Monitor */ + void Monitor(void); + Monitor(); + } + + else + /* Call Emulator directly */ + Emulator(); + + return EXIT_SUCCESS; +} diff --git a/saturn.msf b/saturn.msf new file mode 100644 index 0000000..427aad7 --- /dev/null +++ b/saturn.msf @@ -0,0 +1,33 @@ +$ .+ +$ .identifier : $Id: saturn.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HP48 emulator +$ .title : $RCSfile: saturn.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 8-Sep-2000 +$ .keywords : * +$ .description : +$ Message catalog source file for the main program. +$ .notes : +$ . $Log: saturn.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.9 2000/10/24 16:11:48 cibrario +$ . Added messages 10/501 and 10/502 as suggested by GPL +$ . +$ . Revision 3.8 2000/10/19 15:01:20 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 2.1 2000/09/08 15:35:43 cibrario +$ *** empty log message *** +$ .- + +$set 1 +10 Main + +$set 10 +501 saturn %s - A poor-man's emulator of HP48GX, HP49, HP39/40\nCopyright (C) 1998-2000 Ivan Cibrario Bertolotti\n +502 This program is free software, and comes with ABSOLUTELY NO WARRANTY;\nfor details see the accompanying documentation.\n\n diff --git a/saturn.texi b/saturn.texi new file mode 100644 index 0000000..1e08da6 --- /dev/null +++ b/saturn.texi @@ -0,0 +1,150 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename saturn.info +@settitle Saturn User Manual +@iftex +@afourpaper +@end iftex +@c @setchapternewpage odd @c (deprecated in Texinfo documentation) +@c %**end of header + +@c $Id: saturn.texi,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + +@c --------------------------------------------------------------------------- + + +@ifinfo +This file documents saturn, a poor-man's emulator of the HP48GX, +HP49, and HP40 calculators for Unix systems. + +Copyright 1998-2000 Ivan Cibrario Bertolotti + +Permission is granted to make and distribute verbatim +copies of this manual provided the copyright notice and +this permission notice are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX +and print the results, provided the printed document +carries a copying permission notice identical to this +one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified +versions of this manual under the conditions for +verbatim copying, provided also that the section +entitled ``GNU General Public License'' +is included exactly as in the original, and provided +that the entire resulting derived work is distributed +under the terms of a permission notice identical to this +one. + +Permission is granted to copy and distribute +translations of this manual into another language, +under the above conditions for modified versions, +except that this permission notice may be stated in a +translation approved by original copyright holder. +@end ifinfo + + +@c --------------------------------------------------------------------------- + + +@titlepage +@title @code{saturn} User Manual +@subtitle $Revision: 4.1 $ +@subtitle (for @code{saturn} release @code{4.1}) +@author Ivan Cibrario Bertolotti + +@c Copyright page +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1998-2000 Ivan Cibrario Bertolotti + +Permission is granted to make and distribute verbatim +copies of this manual provided the copyright notice and +this permission notice are preserved on all copies. + +Permission is granted to copy and distribute modified +versions of this manual under the conditions for +verbatim copying, provided also that the section +entitled ``GNU General Public License'' +is included exactly as in the original, and provided +that the entire resulting derived work is distributed +under the terms of a permission notice identical to this +one. + +Permission is granted to copy and distribute +translations of this manual into another language, +under the above conditions for modified versions, +except that this permission notice may be stated in a +translation approved by the original copyright holder. +@end titlepage + + +@c --------------------------------------------------------------------------- + + +@node Top, Introduction and License Conditions, (dir), (dir) + +@ifinfo +This document describes saturn, a poor-man's emulator of the HP48GX, +HP49, and HP40 calculators for Unix-like systems, and applies to +revision 4.1 of the software. +@end ifinfo + +@menu +* Introduction and License Conditions:: +* Preparing saturn for Use:: +* Using the Emulator:: +* Command Line Options:: +* Customizing saturn:: +* The sutil Library:: +* Tips Tricks and Known Bugs:: +* GNU GENERAL PUBLIC LICENSE:: +* Concept Index:: +@end menu + +@c --------------------------------------------------------------------------- + +@include introd.texi + +@c --------------------------------------------------------------------------- + +@include prep.texi + +@c --------------------------------------------------------------------------- + +@include using.texi + +@c --------------------------------------------------------------------------- + +@include clopt.texi + +@c --------------------------------------------------------------------------- + +@include custom.texi + +@c --------------------------------------------------------------------------- + +@include sutil.texi + +@c --------------------------------------------------------------------------- + +@include tips.texi + +@c --------------------------------------------------------------------------- + +@c GNU General Public License +@include gpl.texi + +@c --------------------------------------------------------------------------- + +@include cindex.texi + +@c --------------------------------------------------------------------------- + +@c The End +@contents +@bye diff --git a/serial.c b/serial.c new file mode 100644 index 0000000..f3d78ae --- /dev/null +++ b/serial.c @@ -0,0 +1,1247 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: serial.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: serial.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 13-Sep-2000 +.keywords : * +.description : + + SASM.DOC by HP (HORN disk 4) + Guide to the Saturn Processor Rev. 0.00f and 1.0b by Matthew Mastracci + entries.srt by Mika Heiskanen (mheiskan@vipunen.hut.fi) + x48 source code by Eddie C. Dost (ecd@dressler.de) + Emu48 source code by Sebastien Cariler + +.include : config.h, machdep.h, serial.h + +.notes : + $Log: serial.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.17 2000/11/23 17:01:45 cibrario + Implemented sutil library and assorted bug fixes: + - Fixed UpdateTCS macro; it gave wrong results when trb was full + - in SerialInit(), ensure that the pty is fully transparent by default + - in SerialInit(), slave pty must have O_NONBLOCK set, otherwise + SerialClose() could hang (rare) + - in HandleSerial(), transmit ring buffer must be emptied even + when !IOC_SON + + Revision 3.16 2000/11/21 16:41:08 cibrario + Ultrix/IRIX support: + - Added sgi/IRIX support (USE_STREAMSPTY) + - Added fallback, dummy pty implementation + + Revision 3.10 2000/10/24 16:14:58 cibrario + Added/Replaced GPL header + + Revision 3.5 2000/10/02 09:51:21 cibrario + Linux support: + - libc6 >= 2.0 has openpty() + + Revision 3.2 2000/09/22 14:34:55 cibrario + Implemented preliminary support of HP49 hw architecture: + - Conditionally (#ifdef HP49_SUPPORT) simplified handling of + RCS_RBZ and RCS_RBF bits of RCS register. + - Conditionally (#ifdef HP49_SUPPORT) disabled local ECHO on master + pty when USE_OPENPTY is in effect; this avoid spurious rx + when no process is connected to the slave pty yet. Apparently, + USE_STREAMSPTY does not suffer from this. + - Conditionally (#ifdef HP49_SUPPORT) removed warning message + when reading from an empty RRB. + + * Revision 2.6 2000/09/15 09:23:02 cibrario + * - Implemented USE_STREAMSPTY (needed to build saturn on Solaris) + * - Avoided name clash on ADDRESS_MASK when including pty headers + * (Digital UNIX platform) + * - Enhanced documentation of public functions + * + * Revision 2.5 2000/09/14 15:44:08 cibrario + * *** empty log message *** + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: serial.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "serial.h" /* 2.5: Serial port emulation module */ +#include "debug.h" + +#include "args.h" + +#define CHF_MODULE_ID SERIAL_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Determine pty implementation + + Currently, the following two implementations are supported: + + - openpty(); available on Digital UNIX and Linux + - STREAMS pseudo-tty driver (/dev/ptmx); available on Solaris 2.6 + + SERIAL_FORCE_OPENPTY and SERIAL_FORCE_STREAMSPTY can be defined in config.h + to force this module to use a particular implementation; otherwise, + a (hopefully) appropriate implementation will be automatically selected + by the cpp code that follows, depending on the build platform. + + At the end of the cpp code below, exactly one of the macros + USE_OPENPTY and USE_STREAMSPTY will be defined; they should be used + to conditionally select the implementation-dependent portions of code. + + ---------------------------------------------------------------------------*/ + +#ifdef SERIAL_FORCE_OPENPTY +# define USE_OPENPTY +# define FORCED +#endif + +#ifdef SERIAL_FORCE_STREAMSPTY +# define USE_STREAMSPTY +# define FORCED +#endif + +/* 3.5: Added linux support (__linux__ cpp macro) */ +#if (defined(__osf__) || defined(__linux__)) && !defined(FORCED) +# define USE_OPENPTY +#endif + +/* 3.16: Added sgi support (__sgi cpp macro) */ +#if (defined(__sun__) || defined(__sgi)) && !defined(FORCED) +# define USE_STREAMSPTY +#endif + +/* 3.16: If no appropriate pty implementation has been found, + throw a dummy implementation in. +*/ +#if !defined(USE_OPENPTY) && !defined(USE_STREAMSPTY) +# define USE_NOPTY +#endif + +#undef FORCED + + +/*--------------------------------------------------------------------------- + Include pty implementation-specific headers and definitions + ---------------------------------------------------------------------------*/ + +#ifdef USE_OPENPTY +#undef ADDRESS_MASK /* 2.6: Avoid name clash 8-( */ +#include /* fcntl() */ +#include /* ttyname() */ +#include /* openpty() */ +#include /* tcgetattr()/tcsetattr() */ +#include +#include +#endif + +#ifdef USE_STREAMSPTY +#undef ADDRESS_MASK /* 2.6: Avoid name clash 8-( */ +/* stdlib.h already included */ +#include /* open(), fcntl() */ +#include +#include /* tcgetattr()/tcsetattr() */ +#include /* ioctl() */ +#define PTY_MASTER "/dev/ptmx" /* Master cloning device */ +#endif + + +/*--------------------------------------------------------------------------- + Private implementation of ring buffers + + The following data type definitions, macros, and functions together + implement the ring buffers used to hold serial data being transmitted + and received. Multibyte pushes and pops are efficiently supported: + the whole ring buffer can be filled/emptied completely with only + two, non-overlapping memcpy(), read() or write() operations. + + ---------------------------------------------------------------------------*/ + +#define RB_SIZE 1024 /* Buffer size (# of characters) */ + +/* Hello, I am a RingBuffer... 8-} */ +struct RingBuffer +{ + int n; /* Number of full slots: 0 <= n <= RB_SIZE */ + int8 *rp, *wp; /* Read/Write pointers */ + int8 *ep; /* Pointer to the end of .data[] */ + int8 data[RB_SIZE]; /* Buffer storage */ +}; + +/* Basic macros: + + Min(a, b) returns the minimum (as told by '<') between a and b; + warning: evaluates a and b twice. + + ReadPointer(rb) returns the read pointer of a given buffer; it + points to ContFullSlots() full buffer slots. + + FullSlots(rb) returns the number of full slots of a given buffer; + the slots are not necessarily contiguous. + + ContFullSlots(rb) returns the number of *contiguous* full slots of a + given buffer, starting at the current ReadPointer; + this macro is guaranteed to return a strictly + positive value if the buffer is not empty. + + EmptySlots(rb) returns the number of empty slots of a given buffer; + the slots are not necessarily contiguous. + + ContEmptySlots(rb) returns the number of *contiguous* empty slots of a + given buffer, starting at the current WritePointer; + this macro is guaranteed to return a strictly + positive value if the buffer is not full. + + UpdateReadPointer(rb, n) + moves the read pointer of ring buffer rb n slots + forward + + UpdateWritePointer(rb, n) + moves the write pointer of ring buffer rb n slots + forward + + Push(rb, c) pushes character c into ring buffer rb; this macro + must be invoked only if EmptySlots(rb) is strictly + positive. + + Pull(rb, cp) pulls a character from ring buffer rb and stores it + into *cp; this macro must be invoked only if + EmptySlots(rb) is strictly positive. + + InitRingBuffer(rb) initializes ring buffer rb; it must be called + before using the ring buffer in any way. +*/ +#define Min(a, b) (((a) < (b)) ? (a) : (b)) +#define ReadPointer(rb) ((rb).rp) +#define FullSlots(rb) ((rb).n) +#define ContFullSlots(rb) (Min(FullSlots(rb), (rb).ep - ReadPointer(rb))) +#define WritePointer(rb) ((rb).wp) +#define EmptySlots(rb) (RB_SIZE - FullSlots(rb)) +#define ContEmptySlots(rb) (Min(EmptySlots(rb), (rb).ep - WritePointer(rb))) + +#define UpdateReadPointer(rb, n) \ +{ \ + FullSlots(rb) -= (n); \ + ReadPointer(rb) += (n); \ + if(ReadPointer(rb) >= (rb).ep) ReadPointer(rb) -= RB_SIZE; \ +} + +#define UpdateWritePointer(rb, n) \ +{ \ + FullSlots(rb) += (n); \ + WritePointer(rb) += (n); \ + if(WritePointer(rb) >= (rb).ep) WritePointer(rb) -= RB_SIZE; \ +} + +#define Push(rb, c) \ +{ \ + FullSlots(rb)++; \ + *(WritePointer(rb)++) = c; \ + if(WritePointer(rb) >= (rb).ep) WritePointer(rb) -= RB_SIZE; \ +} + +#define Pull(rb, cp) \ +{ \ + FullSlots(rb)--; \ + *cp = *(ReadPointer(rb)++); \ + if(ReadPointer(rb) >= (rb).ep) ReadPointer(rb) -= RB_SIZE; \ +} + +#define InitRingBuffer(rb) \ +{ \ + FullSlots(rb) = 0; \ + ReadPointer(rb) = WritePointer(rb) = (rb).data; \ + (rb).ep = (rb).data + RB_SIZE; \ +} + +/* Push/Pull functions: + + PullAndWrite(rbp, fd) + This function pulls as many characters as possible from the ring buffer + pointed by rbp and write()s them into file descriptor fd. Returns the + number of characters actually written, or a negative integer if + write() reported an error. + + ReadAndPush(rbp, fd) + This function reads as many characters as possible from file descriptor fd + and pushes them into the ring buffer pointed by rbp. Returns the + number of characters actually read, or a negative integer if + read() reported an error. + +*/ +static int PullAndWrite(struct RingBuffer *rbp, int fd) +{ + int total = 0; /* Total # of chars written */ + + while(FullSlots(*rbp) > 0) + { + int chunk = ContFullSlots(*rbp); /* Chunk size */ + int result; /* # of chars written */ + + /* write() takes its data from ReadPointer (full slots) */ + result = write(fd, ReadPointer(*rbp), chunk); + + if(result < 0 && errno != EAGAIN) + /* write() failed; return an error indication */ + return -1; + + if(result > 0) + { + /* write() wrote at least one character; update ReadPointer */ + total += result; + UpdateReadPointer(*rbp, result); + } + + if(result != chunk) + /* Partial success of write(); break loop for now */ + break; + } + + return total; +} + +static int ReadAndPush(struct RingBuffer *rbp, int fd) +{ + int total = 0; /* Total # of chars read */ + + while(EmptySlots(*rbp) > 0) + { + int chunk = ContEmptySlots(*rbp); /* Chunk size */ + int result; /* # of chars read */ + + /* read() puts its data into WritePointer (empty slots) */ + result = read(fd, WritePointer(*rbp), chunk); + + if(result < 0 && errno != EAGAIN) + /* read() failed; return an error indication */ + return -1; + + if(result > 0) + { + /* read() read at least one character; update WritePointer */ + total += result; + UpdateWritePointer(*rbp, result); + } + + if(result != chunk) + /* Partial success of read(); break loop */ + break; + } + + return total; +} + + +/*--------------------------------------------------------------------------- + Static variables, holding the status of the emulated port + ---------------------------------------------------------------------------*/ + +static Nibble ioc = 0; /* I/O and interrupt control register */ +#define IOC_SON 0x08 /* Serial port enable */ +#define IOC_ETBE 0x04 /* Enable IRQ on TX buffer empty */ +#define IOC_ERBF 0x02 /* Enable IRQ on RX buffer full */ +#define IOC_ERBZ 0x01 /* Enable IRQ on RX buzy (sic) */ + +static Nibble rcs = 0; /* RX control & status register */ +#define RCS_UNUSED 0x08 /* Unused (?) */ +#define RCS_RER 0x04 /* RX Error */ +#define RCS_RBZ 0x02 /* RX Buzy */ +#define RCS_RBF 0x01 /* RX Buffer full */ + +static Nibble tcs = 0; /* TX control & status register */ +#define TCS_BRK 0x08 /* Unknown (?) */ +#define TCS_LPB 0x04 /* Unknown (?) */ +#define TCS_TBZ 0x02 /* TX Buzy */ +#define TCS_TBF 0x01 /* TX Buffer full */ + +static struct RingBuffer rrb; /* RX ring buffer */ +static struct RingBuffer trb; /* TX ring buffer */ + +static char *pty_name; /* Name of pty's slave side */ +static int master_pty; /* File descriptor of pty's master side */ +static int slave_pty; /* File descriptor of pty's slave side */ + + +/*--------------------------------------------------------------------------- + Helper macros + + CheckIRQ This macro checks the current status of ioc, rcs, and tcs, + and posts an interrupt request if appropriate. It should be + called by all functions that modify the above-mentioned + registers, after the modification has been made. + Interrupt requests are posted only when IOC_SON is set. + + UpdateRCS This macro updates the rcs register according to the + current rrb FullSlots: + - if the receiver ring buffer holds more than 1 character, + RCS_RBZ and RCS_RBF are both set + - if the receiver ring buffer holds exactly 1 character, + RCS_RBZ is reset and RCS_RBF is set + - if the receiver ring buffer is empty, + RCS_RBZ and RCS_RBF are both reset + + 3.2: If HP49_SUPPORT is enabled, the RCS_RBZ bit is + always left clear and only RCS_RBF is updated; + this is simpler and works well on the 48, too. + + UpdateTCS This macro updates the tcs register according to the + current trb EmptySlots: + - if the transmitter ring buffer has more than 1 empty slot, + TCS_TBZ and TCS_TBF are both reset + - if the transmitter ring buffer has 1 empty slot, + TCS_TBZ is set and TCS_TBF is reset + - if the transmitter ring buffer is full, + TCS_TBZ and TCS_TBF are both set. + + ---------------------------------------------------------------------------*/ + +#define CheckIRQ \ +if((ioc & IOC_SON) \ + && ( \ + ((ioc & IOC_ETBE) && (! (tcs & TCS_TBF))) \ + || ((ioc & IOC_ERBF) && (rcs & RCS_RBF)) \ + || ((ioc & IOC_ERBZ) && (rcs & RCS_RBZ)) \ + )) CpuIntRequest(INT_REQUEST_IRQ) + +#ifdef HP49_SUPPORT +#define UpdateRCS \ +if(FullSlots(rrb) > 0) \ +{ \ + rcs |= RCS_RBF; \ +} \ +else \ +{ \ + rcs &= ~(RCS_RBF); \ +} +#else +#define UpdateRCS \ +if(FullSlots(rrb) > 1) \ +{ \ + rcs |= (RCS_RBF|RCS_RBZ); \ +} \ +else if(FullSlots(rrb) > 0) \ +{ \ + rcs |= RCS_RBF; \ + rcs &= ~RCS_RBZ; \ +} \ +else \ +{ \ + rcs &= ~(RCS_RBF|RCS_RBZ); \ +} +#endif + +#define UpdateTCS \ +if(EmptySlots(trb) > 1) \ +{ \ + tcs &= ~(TCS_TBF|TCS_TBZ); \ +} \ +else if(EmptySlots(trb) > 0) \ +{ \ + tcs &= ~TCS_TBF; \ + tcs |= TCS_TBZ; \ +} \ +else \ +{ \ + tcs |= (TCS_TBF|TCS_TBZ); \ +} + + +/*--------------------------------------------------------------------------- + Public functions + ---------------------------------------------------------------------------*/ + + +/* .+ + +.title : SerialInit +.kind : C function +.creation : 13-Sep-2000 +.description : + This function opens and initializes the pseudo-terminal that will + be used to emulate the calculator's serial port. When successful + it returns to the caller the symbolic name of the slave side of + the pseudo-terminal (that is, the side that will actually be used + by file transfer programs such as kermit), otherwise it signals + one or more status codes and returns NULL. + + Notice that the string returned by SerialInit() shall not be + modified in any way. + +.call : + slave_pty_name = SerialInit(void); +.input : + void +.output : + const char *slave_pty_name, name of slave pty or NULL +.status_codes : + SERIAL_I_CALLED + SERIAL_I_PTYNAME + SERIAL_W_NOPTY + SERIAL_F_OPENPTY + SERIAL_F_FCNTL + SERIAL_F_OPEN_MASTER + SERIAL_F_GRANTPT + SERIAL_F_UNLOCKPT + SERIAL_F_OPEN_SLAVE + SERIAL_F_PUSH +.notes : + 2.5, 13-Sep-2000, creation + 2.6, 15-Sep-2000, update + - implemented USE_STREAMSPTY + 3.2, 22-Sep-2000, update + - conditionally (#ifdef HP49_SUPPORT) disabled local ECHO on master + pty when USE_OPENPTY is in effect; this avoid spurious rx + when no process is connected to the slave pty yet. + 3.16, 16-Nov-2000, update + - added dummy pty implementation + 3.17, 22-Nov-2000, bug fix + - ensure that the pty is fully transparent by default + - slave pty must have O_NONBLOCK set +.- */ +const char *SerialInit(void) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "SerialInit"); + + /* Initialize ring buffers */ + InitRingBuffer(rrb); + InitRingBuffer(trb); + +#ifndef USE_NOPTY +#ifdef USE_OPENPTY + /* Open pty master/slave pair; don't specify pty name, struct termios + and struct winsize. + */ + if(openpty(&master_pty, &slave_pty, NULL, NULL, NULL)) + { + pty_name = (char *)NULL; + + ChfErrnoCondition; + ChfCondition SERIAL_F_OPENPTY, CHF_FATAL ChfEnd; + ChfSignal(); + } + + else + { + int cur_flags; + + pty_name = ttyname(slave_pty); + + /* Remember: close the pty before exiting */ + atexit(SerialClose); + + /* Set O_NONBLOCK on master_pty */ + if((cur_flags = fcntl(master_pty, F_GETFL, 0)) < 0 + || fcntl(master_pty, F_SETFL, cur_flags|O_NONBLOCK) < 0) + { + ChfErrnoCondition; + ChfCondition SERIAL_F_FCNTL, CHF_FATAL ChfEnd; + ChfSignal(); + } + } +#endif + +#ifdef USE_STREAMSPTY + /* Open master cloning device */ + if((master_pty = open(PTY_MASTER, O_RDWR|O_NONBLOCK)) < 0) + { + pty_name = (char *)NULL; + + ChfErrnoCondition; + ChfCondition SERIAL_F_OPEN_MASTER, CHF_FATAL, PTY_MASTER ChfEnd; + ChfSignal(); + } + + else + { + /* Master side opened ok; change permissions and unlock slave side */ + + if(grantpt(master_pty) < 0) + { + /* close() may modify errno; save it first */ + ChfErrnoCondition; + + (void)close(master_pty); + + ChfCondition SERIAL_F_GRANTPT, CHF_FATAL ChfEnd; + ChfSignal(); + } + + if(unlockpt(master_pty) < 0) + { + /* close() may modify errno; save it first */ + ChfErrnoCondition; + + (void)close(master_pty); + + ChfCondition SERIAL_F_UNLOCKPT, CHF_FATAL ChfEnd; + ChfSignal(); + } + + /* Get name of slave side; this must be done on the *master* side */ + pty_name = ptsname(master_pty); + + /* Open slave in nonblocking mode */ + if((slave_pty = open(pty_name, O_RDWR|O_NONBLOCK)) < 0) + { + /* close() may modify errno; save it first */ + ChfErrnoCondition; + + (void)close(master_pty); + + ChfCondition SERIAL_F_OPEN_SLAVE, CHF_FATAL, pty_name ChfEnd; + ChfSignal(); + } + + /* Remember: close the pty before exiting */ + atexit(SerialClose); + + /* Push appropriate STREAMS modules on the slave side to support + terminal emulation. This way, the slave side should be + indistinguishable from a real terminal. + */ + if(ioctl(slave_pty, I_PUSH, "ptem") == -1) + { + ChfErrnoCondition; + ChfCondition SERIAL_F_PUSH, CHF_FATAL, "ptem" ChfEnd; + ChfSignal(); + } + + if(ioctl(slave_pty, I_PUSH, "ldterm") == -1) + { + ChfErrnoCondition; + ChfCondition SERIAL_F_PUSH, CHF_FATAL, "ldterm" ChfEnd; + ChfSignal(); + } + + } +#endif + +#ifdef HP49_SUPPORT + /* 3.17: Ensure that the pty is fully trasparent by default. + This allows to use most non-terminal-aware applications (such as od) + on the pty directly. + */ + { + struct termios tios; + + if(tcgetattr(slave_pty, &tios)) + { + ChfErrnoCondition; + ChfCondition SERIAL_F_TCGETATTR, CHF_FATAL ChfEnd; + ChfSignal(); + } + + tios.c_iflag &= ~(BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR + |IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF|IMAXBEL); + + tios.c_iflag |= IGNBRK; + + tios.c_oflag &= ~(OPOST|OLCUC|ONLCR|OCRNL|ONOCR|ONLRET|OFILL + |OFDEL|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); + + tios.c_cflag &= ~(CSIZE|CSTOPB|PARENB|PARODD); + + tios.c_cflag |= CS8|CREAD|HUPCL|CLOCAL; + + tios.c_lflag &= ~(ISIG|ICANON|ECHO|ECHONL|IEXTEN); + + /* read()s are satisfed when at least 1 character is available; + intercharacter/read timer disabled. + */ + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + if(tcsetattr(slave_pty, TCSANOW, &tios)) + { + ChfErrnoCondition; + ChfCondition SERIAL_F_TCSETATTR, CHF_FATAL ChfEnd; + ChfSignal(); + } + + } +#endif + + /* Publish pty name */ + ChfCondition SERIAL_I_PTY_NAME, CHF_INFO, pty_name ChfEnd; + ChfSignal(); + +#else + /* Dummy implementation; do nothing */ + pty_name = ""; + + ChfCondition SERIAL_W_NOPTY, CHF_WARNING ChfEnd; + ChfSignal(); +#endif + + return pty_name; +} + + +/* .+ + +.title : SerialClose +.kind : C function +.creation : 13-Sep-2000 +.description : + This function closes the pseudo-terminal opened by SerialInit(), if + SerialInit() succeeded, it does nothing otherwise. + + Notice that is is normally unnecessary to call this function directly, + because SerialInit() automatically registers it with atexit() upon + successful completion. + +.call : + SerialClose(); +.input : + void +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_E_PTY_CLOSE +.notes : + 2.5, 13-Sep-2000, creation + 2.6, 15-Sep-2000, update + - updated documentation + +.- */ +void SerialClose(void) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "SerialClose"); + + if(close(slave_pty) || close(master_pty)) + { + ChfErrnoCondition; + ChfCondition SERIAL_E_PTY_CLOSE, CHF_ERROR ChfEnd; + ChfSignal(); + } +} + + +/* .+ + +.title : SerialPtyName +.kind : C function +.creation : 13-Sep-2000 +.description : + This function returns to the caller the name of the slave size of + the pseudo-terminal previously opened by SerialInit() if the + latter function succeeded, otherwise NULL. + + Notice that the string returned by SerialPtyName() shall not be + modified in any way. + +.call : + slave_pty_name = SerialPtyName(); +.input : + void +.output : + const char *slave_pty_name, name of slave pty opened by + SerialInit() +.status_codes : + SERIAL_I_CALLED +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +const char *SerialPtyName(void) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "SerialPtyName"); + + return pty_name; +} + + +/* .+ + +.title : Serial_IOC_Read +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a read of the serial I/O and interrupt control + register and returns its current value to the caller. + + Reading IOC returns the current value of the ioc emulation register + and does not trigger any ancillary action. + +.call : + n = Serial_IOC_Read(); +.input : + void +.output : + Nibble n, current value of emulated register +.status_codes : + SERIAL_I_CALLED + SERIAL_I_READ +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +Nibble Serial_IOC_Read(void) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_IOC_Read"); + debug2(DEBUG_C_SERIAL, SERIAL_I_READ, "IOC", ioc); + + return ioc; +} + + +/* .+ + +.title : Serial_RCS_Read +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a read of the serial receiver control & status + register and returns its current value to the caller. + + Reading RCS returns the current value of the rcs emulation register + and does not trigger any ancillary action. + +.call : + n = Serial_RCS_Read(); +.input : + void +.output : + Nibble n, current value of emulated register +.status_codes : + SERIAL_I_CALLED + SERIAL_I_READ +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +Nibble Serial_RCS_Read(void) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_RCS_Read"); + debug2(DEBUG_C_SERIAL, SERIAL_I_READ, "RCS", rcs); + + return rcs; +} + + +/* .+ + +.title : Serial_TCS_Read +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a read of the serial transmitter control & status + register and returns its current value to the caller. + + Reading TCS returns the current value of the tcs emulation register + and does not trigger any ancillary action. + +.call : + n = Serial_TCS_Read(); +.input : + void +.output : + Nibble n, current value of emulated register +.status_codes : + SERIAL_I_CALLED + SERIAL_I_READ +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +Nibble Serial_TCS_Read(void) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_TCS_Read"); + debug2(DEBUG_C_SERIAL, SERIAL_I_READ, "TCS", tcs); + + return tcs; +} + + +/* .+ + +.title : Serial_RBR_Read +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a read of the serial receiver buffer + register and returns its current value to the caller. + + Reading RBR triggers the following additional actions: + - pulls one character from receiver ring buffer + - UpdateRCS + - CheckIRQ + +.call : + d = Serial_RBR_Read(); +.input : + void +.output : + int8 d, current value of emulated register +.status_codes : + SERIAL_I_CALLED + SERIAL_I_RBR + SERIAL_W_EMPTY_RRB +.notes : + 2.5, 13-Sep-2000, creation + 3.2, 22-Sep-2000, update + - conditionally (#ifdef HP49_SUPPORT) removed warning message + when reading from an empty RRB. +.- */ +int8 Serial_RBR_Read(void) +{ + int8 rx; + + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_RBR_Read"); + + /* Pull one character from rbr, if not empty */ + if(FullSlots(rrb) > 0) + { + Pull(rrb, &rx); + } + + else + { + /* rrb is empty */ + +#ifndef HP49_SUPPORT + /* 3.2: The HP49 firmware (1.19-4) can read from an empty RRB; + this is not harmful, and this warning can be removed. + */ + ChfCondition SERIAL_W_EMPTY_RRB, CHF_WARNING, rcs ChfEnd; + ChfSignal(); +#endif + + rx = (int8)0xFF; + } + + + /* Update receiver status */ + UpdateRCS; + + /* Post a new IRQ if necessary */ + CheckIRQ; + + debug1(DEBUG_C_SERIAL, SERIAL_I_RBR, rx); + return rx; +} + + +/* .+ + +.title : Serial_IOC_Write +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a write into the serial I/O and interrupt control + register. + + Writing IOC triggers the following additional actions: + + - CheckIRQ is executed + +.call : + Serial_IOC_Write(n); +.input : + Nibble n, value to be written into the emulated register +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_I_WRITE +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +void Serial_IOC_Write(Nibble n) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_IOC_Write"); + debug3(DEBUG_C_SERIAL, SERIAL_I_WRITE, "IOC", ioc, n); + + ioc = n; + + CheckIRQ; +} + + +/* .+ + +.title : Serial_RCS_Write +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a write into the serial receiver control & status + register. + + The status is updated and no additional actions are taken; it is not + so clear when a direct write into RCS could be useful. + +.call : + Serial_RCS_Write(n); +.input : + Nibble n, value to be written into the emulated register +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_I_WRITE +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +void Serial_RCS_Write(Nibble n) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_RCS_Write"); + debug3(DEBUG_C_SERIAL, SERIAL_I_WRITE, "RCS", rcs, n); + + rcs = n; +} + + +/* .+ + +.title : Serial_TCS_Write +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a write into the serial transmitter control & status + register. + + The status is updated and no additional actions are taken; it is not + so clear when a direct write into TCS could be useful. + +.call : + Serial_TCS_Write(n); +.input : + Nibble n, value to be written into the emulated register +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_I_WRITE +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +void Serial_TCS_Write(Nibble n) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_TCS_Write"); + debug3(DEBUG_C_SERIAL, SERIAL_I_WRITE, "TCS", tcs, n); + + tcs = n; +} + + +/* .+ + +.title : Serial_CRER_Write +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a write into the serial 'clear receiver error' + register. + + The value written is ignored, and the RCS_RER bit is cleared. + +.call : + Serial_CRER_Write(n); +.input : + Nibble n, value to be written into the emulated register +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_I_WRITE +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +void Serial_CRER_Write(Nibble n) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_CRER_Write"); + debug3(DEBUG_C_SERIAL, SERIAL_I_WRITE, "CRER", 0, n); + + rcs &= ~RCS_RER; +} + + +/* .+ + +.title : Serial_TBR_Write +.kind : C function +.creation : 13-Sep-2000 +.description : + This function emulates a write into the serial transmitter buffer + register. + + Writing RBR triggers the following additional actions: + - pushes one character into transmitter ring buffer + - UpdateTCS + - CheckIRQ + +.call : + Serial_TBR_Write(d); +.input : + int8 d, value to be written into the emulated register +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_I_TBR + SERIAL_W_FULL_TRB +.notes : + 2.5, 13-Sep-2000, creation + +.- */ +void Serial_TBR_Write(int8 d) +{ + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "Serial_TBR_Write"); + debug1(DEBUG_C_SERIAL, SERIAL_I_TBR, d); + + /* Pull one character from rbr, if not empty */ + if(EmptySlots(trb) > 0) + { + Push(trb, d); + } + + else + { + /* trb is full; discard character */ + ChfCondition SERIAL_W_FULL_TRB, CHF_WARNING, tcs ChfEnd; + ChfSignal(); + } + + /* Update transmitter status */ + UpdateTCS; + + /* Post a new IRQ if necessary */ + CheckIRQ; +} + + +/* .+ + +.title : HandleSerial +.kind : C function +.creation : 14-Sep-2000 +.description : + This function is called by the emulator loop every 1/16s, and performs + the following actions: + + - attempt to pull as many characters as possible from the transmitter + ring buffer and to write them on master_pty (PullAndWrite) + - UpdateTCS + + - attempt to read as many characters are possible from master_pty and + to push them into the receiver ring buffer (ReadAndPush) + - UpdateRCS + + - CheckIRQ + +.call : + HandleSerial(); +.input : + void +.output : + void +.status_codes : + SERIAL_I_CALLED + SERIAL_E_TRB_DRAIN + SERIAL_E_RRB_CHARGE +.notes : + 2.5, 14-Sep-2000, creation + 2.6, 15-Sep-2000, update + - enhanced documentation of status_codes + 3.16, 16-Nov-2000, update + - added dummy pty implementation + 3.17, 22-Nov-2000, bug fix + - transmit ring buffer must be emptied even when !IOC_SON +.- */ +void HandleSerial(void) +{ + int result; + + debug1(DEBUG_C_TRACE, SERIAL_I_CALLED, "HandleSerial"); + +#ifndef USE_NOPTY + /* Attempt to drain transmitter buffer even if serial port is closed */ + result = PullAndWrite(&trb, master_pty); + + /* Signal a condition upon failure */ + if(result < 0) + { + ChfErrnoCondition; + ChfCondition SERIAL_E_TRB_DRAIN, CHF_ERROR ChfEnd; + ChfSignal(); + } + + /* Update tcs */ + UpdateTCS; + + if(ioc & IOC_SON) + { + /* Attempt to charge receiver buffer */ + result = ReadAndPush(&rrb, master_pty); + + /* Signal a condition upon failure */ + if(result < 0) + { + ChfErrnoCondition; + ChfCondition SERIAL_E_RRB_CHARGE, CHF_ERROR ChfEnd; + ChfSignal(); + } + + /* Update receiver status */ + UpdateRCS; + + /* Post an IRQ if necessary */ + CheckIRQ; + } +#endif + +} diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..2001c96 --- /dev/null +++ b/serial.h @@ -0,0 +1,144 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: serial.h,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: serial.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 13-Sep-2000 +.keywords : * +.description : + This header contains all definitions and declarations related to the + serial port emulation modules of the HP48. References: + + SASM.DOC by HP (HORN disk 4) + Guide to the Saturn Processor Rev. 0.00f by Matthew Mastracci + entries.srt by Mika Heiskanen (mheiskan@vipunen.hut.fi) + x48 source code by Eddie C. Dost (ecd@dressler.de) + +.include : config.h machdep.h cpu.h + +.notes : + $Log: serial.h,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.16 2000/11/21 16:41:44 cibrario + Ultrix/IRIX support: + - New condition code: SERIAL_W_NOPTY + + Revision 3.10 2000/10/24 16:14:59 cibrario + Added/Replaced GPL header + + Revision 3.2 2000/09/22 14:36:53 cibrario + Implemented preliminary support of HP49 hw architecture: + - New status codes: SERIAL_F_TCGETATTR, SERIAL_F_TCSETATTR + + * Revision 2.6 2000/09/15 09:25:06 cibrario + * Added definition of the following status codes: + * SERIAL_F_OPEN_MASTER, SERIAL_F_GRANTPT, SERIAL_F_UNLOCKPT, + * SERIAL_F_OPEN_SLAVE, SERIAL_F_PUSH; the implementation of + * USE_STREAMSPTY needs them. + * + * Revision 2.5 2000/09/14 15:43:57 cibrario + * *** empty log message *** + * + +.- */ + + +/*--------------------------------------------------------------------------- + Macro/Data type definitions + ---------------------------------------------------------------------------*/ + +#define SERIAL_RCS_INFO "$Revision: 4.1 $ $State: Rel $" + + +/*--------------------------------------------------------------------------- + Chf condition codes + ---------------------------------------------------------------------------*/ + +#define SERIAL_I_CALLED 101 /* Function %s called */ +#define SERIAL_I_REVISION 102 /* Serial port emulation rev. %s */ +#define SERIAL_I_READ 103 /* Read %s -> %x */ +#define SERIAL_I_WRITE 104 /* Write %s %x -> %x */ +#define SERIAL_I_RBR 105 /* Read RBR -> %x */ +#define SERIAL_I_TBR 106 /* Write TBR <- %x */ +#define SERIAL_I_PTY_NAME 107 /* Slave pty name is %s */ +#define SERIAL_W_EMPTY_RRB 201 /* Read from empty RX buffer, rcs=%x */ +#define SERIAL_W_FULL_TRB 202 /* Write into full TX buffer, tcs=%x */ +#define SERIAL_W_NOPTY 203 /* 3.16: Pty support not available */ +#define SERIAL_E_TRB_DRAIN 301 /* Error draining TX buffer */ +#define SERIAL_E_RRB_CHARGE 302 /* Error charging RX buffer */ +#define SERIAL_E_PTY_CLOSE 303 /* Error closing pty */ +#define SERIAL_F_OPENPTY 401 /* openpty() failed on master pty */ +#define SERIAL_F_FCNTL 402 /* fcntl() failed on master pty */ +#define SERIAL_F_OPEN_MASTER 403 /* Can't open pty master %s */ +#define SERIAL_F_GRANTPT 404 /* grantpt() failed on master pty */ +#define SERIAL_F_UNLOCKPT 405 /* unlockpt() failed on master pty */ +#define SERIAL_F_OPEN_SLAVE 406 /* Can't open pty slave %s */ +#define SERIAL_F_PUSH 407 /* ioctl(I_PUSH,%s) failed on slave */ +#define SERIAL_F_TCGETATTR 408 /* tcgetattr() failed on master */ +#define SERIAL_F_TCSETATTR 409 /* tcsetattr() failed on master */ + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/* Initialization */ +const char *SerialInit(void); +void SerialClose(void); + +/* Information about slave side of pty */ +const char *SerialPtyName(void); + +/* Register read */ +Nibble Serial_IOC_Read(void); +Nibble Serial_RCS_Read(void); +Nibble Serial_TCS_Read(void); +int8 Serial_RBR_Read(void); + +/* Register write */ +void Serial_IOC_Write(Nibble n); +void Serial_RCS_Write(Nibble n); +void Serial_TCS_Write(Nibble n); +void Serial_CRER_Write(Nibble n); +void Serial_TBR_Write(int8 d); + +/* Event handling */ +void HandleSerial(void); diff --git a/serial.msf b/serial.msf new file mode 100644 index 0000000..b402cea --- /dev/null +++ b/serial.msf @@ -0,0 +1,62 @@ +$ .+ +$ .identifier : $Id: serial.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HP48 emulator +$ .title : $RCSfile: serial.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 13-Sep-2000 +$ .keywords : * +$ .description : +$ Message catalog source file for the serial port emulator. +$ .notes : +$ . $Log: serial.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.16 2000/11/21 16:42:18 cibrario +$ . Ultrix/IRIX support: +$ . - New message: 203 +$ . +$ . Revision 3.8 2000/10/19 15:00:10 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 3.2 2000/09/22 14:38:08 cibrario +$ Implemented preliminary support of HP49 hw architecture: +$ - New messages: 408, 409 +$ . +$ Revision 2.6 2000/09/15 09:26:18 cibrario +$ Added new messages: 403, 404, 405, 406, 407, +$ needed by the implementation of USE_STREAMSPTY +$ . +$ Revision 2.5 2000/09/14 15:44:49 cibrario +$ *** empty log message *** +$ .- + +$set 1 +15 Serial + +$set 15 +101 Function [%s] called +102 Serial port emulator: [%s] +103 Read [%s]; value [%01X] +104 Write [%s]; value [%01X](old) -> [%01X](new) +105 Read RBR; value [%02X] +106 Write TBR; value [%02X] +107 Slave pseudo-terminal name is [%s] +201 Read from empty receiver buffer, rcs [%01X] +202 Write into full transmitter buffer, tcs [%01X] +203 Pseudo-terminals not supported +301 Error draining transmitter buffer +302 Error charging receiver buffer +303 Error shutting down pseudo-terminal +401 openpty() failed on master pty +402 fcntl() failed on master pty +403 Can't open() pty master [%s] +404 grantpt() failed on master pty +405 unlockpt() failed on master pty +406 Can't open() pty slave [%s] +407 ioctl(I_PUSH,[%s]) failed on slave pty +408 tcgetattr() failed on master pty +409 tcsetattr() failed on master pty diff --git a/sutil.texi b/sutil.texi new file mode 100644 index 0000000..9297a68 --- /dev/null +++ b/sutil.texi @@ -0,0 +1,92 @@ +@c $Id: sutil.texi,v 4.1 2000/12/11 09:54:19 cibrario Rel $ + +@node The sutil Library, Tips Tricks and Known Bugs, Customizing saturn, Top +@chapter The sutil Library +@cindex The sutil Library + +The @code{sutil} library is currently available for the HP48 and HP49 +calculators only, and provides the following commands: + +@table @code +@item kget ( '@var{file_name}' -> ) +This command retrieves the file @var{file_name} from mass storage and +stores it into a calculator's variable with the same name. In response +to this command the emulator pops up a file selection box, allowing you +to accept the transfer, possibly altering the source file name/location +proposed by the calculator, or to cancel the transfer. +@item send ( '@var{var_name}' -> ) +This command saves the calculator's variable @var{var_name} into mass +storage. In response to this command the emulator pops up a file +selection box, allowing you to accept the transfer, possibly altering +the target file name/location proposed by the calulator, or to cancel the +transfer. +@item speed ( @var{speed_sel} -> ) +This command allows you to set the speed of the emulated calculator: +@var{speed_sel} must be a real number denoting the desired speed +in MHz; the special value @code{0} indicates that the calculator must +be run to the maximum possible speed. The speed change takes +effect immediately. +@end table + +Of course, these commands work on the @code{saturn} emulator @strong{only}, +and could crash when invoked on another emulator or on the +real calculator. + +You can load the @code{sutil} library into your emulated HP48 by +following these steps: + +@itemize @bullet +@item +Transfer the file @code{sutil_48.lib} into your emulated calculator, +using either xmodem or kermit. +@item +Bring the library on stack level 1 and store it into a port of your choice. +@item +Warmstart the emulated calculator; the @code{sutil} library should +now be in your library catalog. +@end itemize + +To rebuild the library object on your HP48 calculator, the following +steps are required: + +@itemize @bullet +@item +Ensure that both the MetaKernel version 2.30 and the SysRPL entry point +table are loaded on the calculator, and that the @code{STARTEXT} +reserved variable points to the entry point table. +@item +Transfer the source directory @code{sutil_48.dir} into the calculator; +ensure that the transfer is done in ASCII mode. +@item +Enter the source directory and run the @code{MAKE} word; the +@code{sutil} library should now be on stack level 1. +@end itemize + +You can load the @code{sutil} library into your emulated HP49 by +following these steps: + +@itemize @bullet +@item +Transfer the file @code{sutil_49.lib} into your emulated calculator, +using either xmodem or kermit. +@item +Bring the library on stack level 1 and store it into a port of your choice. +@item +Warmstart the emulated calculator; the @code{sutil} library should +now be in your library catalog. +@end itemize + +To rebuild the library object on your HP49 calculator, the following +steps are required: + +@itemize @bullet +@item +Ensure that library 256 is attached and that library 258 (extable) +is loaded on the calculator. +@item +Transfer the source directory @code{sutil_49.dir} into the calculator; +ensure that the transfer is done in ASCII mode. +@item +Enter the source directory and run the @code{MAKE} word; the +@code{sutil} library should now be on stack level 1. +@end itemize diff --git a/sutil_48.dir b/sutil_48.dir new file mode 100644 index 0000000..c923f96 --- /dev/null +++ b/sutil_48.dir @@ -0,0 +1,303 @@ +%%HP: T(3)A(R)F(.); + +@ sutil - A poor-man's utility library for Saturn +@ HP48 Version +@ +@ Original code by Bernard Parisse, Jean-Yves Avenard, and Bob Miller, +@ with minimal adaptations by Ivan Cibrario Bertolotti. +@ +@ $Id: sutil_48.dir,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +@ +@ $Revision: 4.1 $ + + +DIR + @ Build the sutil library from sources in this directory + MAKE + \<< ZAP BUILD + +@ Library creation file +"!ASM +!NO CODE +LIB +; Library definitions +{ " + + 34 CHR + + "sutil - Saturn Utility Library" + + 34 CHR + + + " + 888 + + MESSAGE { + } + + VISIBLE { + kget + send + speed + } + + HIDDEN { + conf + } + + LCONFIG conf +} +@" + + ASM + + \>> + + @ Detach old version of same library, if needed + ZAP + \<< 888 PATH HOME SWAP DETACH EVAL \>> + + @ Build the library components + BUILD + \<< 2. TVARS + 1. + \<< DUP \->STR + IF DUP ".s" POS NOT THEN + DROP2 + ELSE + 1. OVER SIZE 3. - SUB OBJ\-> SWAP RCL ASM SWAP STO + END + \>> DOSUBS + \>> + + @ Generate list of visible library objects + G.VISIBLE + \<< 2. TVARS + 1. + \<< \->STR + IF DUP ".s" POS NOT THEN + DROP + ELSE + 1. OVER SIZE 3. - SUB OBJ\-> + END + \>> DOSUBS + \>> + + @ Source code of kget, MASD syntax + kget.s +" +!RPL +!NO CODE +:: + CK1&Dispatch + id + :: + ' xMEM EvalNoCK % 2000 %- + DUP %0> NcaseSIZEERR + %15 %+ COERCE + + CODE + GOSBVL =SAVPTR + + ; Fetch bint from stack level 1 into A[A] + A=DAT1.A + D1=A + D1=D1+5 + A=DAT1.A + + ; Compute available memory in nibbles into C[A] (C=A*2) + C=A.A + C=C+C.A + + ; Get boundaries of available memory: D0=lower, D1=upper + ; Adjust D1 to leave space for link (5 nibbles) and GC info (1 nibble) + GOSBVL =GETTEMP + D1=D1-6 + + ; Save lower bound into R3[A] + A=D0 + R3=A.A + + ; Save upper bound into R4[A] + A=D1 + R4=A.A + + ; Write a valid object in (bint 0) + LA(5) DOBINT + DAT0=A.A + + D0=D0+5 + + A=0.A + DAT0=A.A + + ; Get rpl pointers back + GOSBVL =GETPTR + + ; Load body address of id in stack level 2 into D1 + D1=D1+5 + A=DAT1.A + D1=A + D1=D1+5 + + ; Load RBR address into D0 + LC(5) =RBR + CD0EX + + ; Load C[A] = upper bound, A[A] = lower bound + A=R4.A + C=A.A + A=R3.A + + ; Write RBR, function code 1 + A=0.S + A=A+1.S + DAT0=A.S + + ; Skip object just loaded, end pointer in D0 + A=R3.A + D0=A + GOSBVL =SKIPOB + + ; Compute object length, including link and GC info, in C[A] + A=D0 + C=A.A + A=R3.A + C=C-A.A + C=C+6.A + + ; Write the link + DAT0=C.A + D0=D0+5 + + ; From supentry list + EQU TEMPTOP $806EE + EQU RSKTOP $806F3 + + ; Recover tempob - was =recover + CD0EX ; C=new temptop, + D=C.A ; save for MOVEDOWN + D0=(5)TEMPTOP + D1=(5)RSKTOP + A=DAT0.A ; old temptop + DAT0=C.A ; set new temptop + D0=A ; set D0 for MOVEDOWN + C=A-C.A ; rsktop adj factor + B=C.A ; B=rsktop adj, A=old temptop + C=DAT1.A ; C=old rsktop + ACEX.A ; switch for convenience + C=A-C.A ; size to move + A=A-B.A ; new rsktop + DAT1=A.A ; set new rsktop + CDEX.A + D1=C ; new temptop for MOVEDOWN + C=D.A ; size to move + GOSBVL =MOVEDOWN ; recover space + GOSBVL =ADJMEM ; adjust memory - was inlined GOVLNG + + ; Restore rpl pointers + GOSBVL =GETPTR + + ; Replace stack level 1 with new object and return to rpl + A=R3.A + DAT1=A.A + GOVLNG =Loop + ENDCODE + + SWAP ' xSTO EvalNoCK + ; +; +@" + + @ Source code of send, MASD syntax + send.s +"!RPL +!NO CODE +:: + CK1&Dispatch + id + :: + DUP ' xRCL EvalNoCK + CODE + GOSBVL =SAVPTR + + ; Save start address into R4[A] + A=DAT1.A + R4=A.A + + ; Skip object; result in D0 + D0=A + GOSBVL =SKIPOB + + ; Load body address of id in stack level 2 into D1 + D1=D1+5 + A=DAT1.A + D1=A + D1=D1+5 + + ; Load start address into A[A] + A=R4 A + + ; Load RBR address into D0 and bring end address back in C[A] + LC(5) =RBR + CD0EX + + ; Write RBR, function code 2 + A=0.S + A=A+2.S + DAT0=A.S + + ; Restore rpl pointers and return to rpl + GOSBVL =GETPTR + GOVLNG =Loop + + ENDCODE + 2DROP + ; +; +@" + + @ Source code of speed, MASD syntax + speed.s +"!RPL +!NO CODE +:: + CK1&Dispatch + real + :: + COERCE + CODE + GOSBVL =SAVPTR + + ; Load bint in stack level 1 into A[A] + A=DAT1.A + D1=A + D1=D1+5 + A=DAT1.A + + ; Load RBR address into D0 + LC(5) =RBR + CD0EX + + ; Write RBR, function code 0; speed is in C[A] + C=A.A + A=0.S + DAT0=A.S + + ; Restore rpl pointers and return to rpl + GOSBVL =GETPTR + GOVLNG =Loop + + ENDCODE + DROP + ; +; +@" + + @ Source code of conf, MASD syntax + conf.s +"!RPL +!NO CODE +:: + # #888 TOSRRP +; +@" + +END diff --git a/sutil_49.dir b/sutil_49.dir new file mode 100644 index 0000000..b729689 --- /dev/null +++ b/sutil_49.dir @@ -0,0 +1,266 @@ +%%HP: T(3)A(R)F(.); + +@ sutil - A poor-man's utility library for Saturn +@ HP49 Version +@ +@ Original code by Bernard Parisse, Jean-Yves Avenard, and Bob Miller, +@ with minimal adaptations by Ivan Cibrario Bertolotti. +@ +@ $Id: sutil_49.dir,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +@ +@ $Revision: 4.1 $ + + +DIR + @ Build the sutil library from sources in this directory + MAKE + \<< ZAP G.VISIBLE '$VISIBLE' STO BUILD CRLIB + \>> + + @ Detach old version of same library, if needed + ZAP + \<< $ROMID PATH HOME SWAP DETACH EVAL \>> + + @ Build the library + BUILD + \<< 2. TVARS + 1. + \<< DUP \->STR + IF DUP ".s" POS NOT THEN + DROP2 + ELSE + 1. OVER SIZE 3. - SUB OBJ\-> SWAP RCL ASM SWAP STO + END + \>> DOSUBS + \>> + + @ Generate list of visible library objects + G.VISIBLE + \<< 2. TVARS + 1. + \<< \->STR + IF DUP ".s" POS NOT THEN + DROP + ELSE + 1. OVER SIZE 3. - SUB OBJ\-> + END + \>> DOSUBS + \>> + + @ Library information + $CONFIG 1. + $ROMID 888. + $TITLE "sutil - Saturn Utility Library" + + @ Source code of kget, MASD syntax + kget.s +" +!RPL +!NO CODE +:: + CK1&Dispatch + id + :: + ' xMEM EvalNoCK % 2000 %- + DUP %0> NcaseSIZEERR + %15 %+ COERCE + + CODE + GOSBVL SAVPTR + + ; Fetch bint from stack level 1 into A[A] + A=DAT1.A + D1=A + D1=D1+5 + A=DAT1.A + + ; Compute available memory in nibbles into C[A] (C=A*2) + C=A.A + C=C+C.A + + ; Get boundaries of available memory: D0=lower, D1=upper + ; Adjust D1 to leave space for link (5 nibbles) and GC info (1 nibble) + GOSBVL GETTEMP + D1=D1-6 + + ; Save lower bound into R3[A] + A=D0 + R3=A.A + + ; Save upper bound into R4[A] + A=D1 + R4=A.A + + ; Write a valid object in (bint 0) + LA(5) DOBINT + DAT0=A.A + + D0=D0+5 + + A=0.A + DAT0=A.A + + ; Get rpl pointers back + GOSBVL GETPTR + + ; Load body address of id in stack level 2 into D1 + D1=D1+5 + A=DAT1.A + D1=A + D1=D1+5 + + ; Load RBR address into D0 + LC(5) RBR + CD0EX + + ; Load C[A] = upper bound, A[A] = lower bound + A=R4.A + C=A.A + A=R3.A + + ; Write RBR, function code 1 + A=0.S + A=A+1.S + DAT0=A.S + + ; Skip object just loaded, end pointer in D0 + A=R3.A + D0=A + GOSBVL SKIPOB + + ; Compute object length, including link and GC info, in C[A] + A=D0 + C=A.A + A=R3.A + C=C-A.A + C=C+6.A + + ; Write the link + DAT0=C.A + D0=D0+5 + + ; From supentry list + EQU TEMPTOP $806EE + EQU RSKTOP $806F3 + + ; Recover tempob - was =recover + CD0EX ; C=new temptop, + D=C.A ; save for MOVEDOWN + D0=(5)TEMPTOP + D1=(5)RSKTOP + A=DAT0.A ; old temptop + DAT0=C.A ; set new temptop + D0=A ; set D0 for MOVEDOWN + C=A-C.A ; rsktop adj factor + B=C.A ; B=rsktop adj, A=old temptop + C=DAT1.A ; C=old rsktop + ACEX.A ; switch for convenience + C=A-C.A ; size to move + A=A-B.A ; new rsktop + DAT1=A.A ; set new rsktop + CDEX.A + D1=C ; new temptop for MOVEDOWN + C=D.A ; size to move + GOSBVL MOVEDOWN ; recover space + GOSBVL ADJMEM ; adjust memory - was inlined GOVLNG + + ; Restore rpl pointers + GOSBVL GETPTR + + ; Replace stack level 1 with new object and return to rpl + A=R3.A + DAT1=A.A + GOVLNG Loop + ENDCODE + + SWAP ' xSTO EvalNoCK + ; +; +@" + + @ Source code of send, MASD syntax + send.s +"!RPL +!NO CODE +:: + CK1&Dispatch + id + :: + DUP ' xRCL EvalNoCK + CODE + GOSBVL SAVPTR + + ; Save start address into R4[A] + A=DAT1.A + R4=A.A + + ; Skip object; result in D0 + D0=A + GOSBVL SKIPOB + + ; Load body address of id in stack level 2 into D1 + D1=D1+5 + A=DAT1.A + D1=A + D1=D1+5 + + ; Load start address into A[A] + A=R4 A + + ; Load RBR address into D0 and bring end address back in C[A] + LC(5) RBR + CD0EX + + ; Write RBR, function code 2 + A=0.S + A=A+2.S + DAT0=A.S + + ; Restore rpl pointers and return to rpl + GOSBVL GETPTR + GOVLNG Loop + + ENDCODE + 2DROP + ; +; +@" + + @ Source code of speed, MASD syntax + speed.s +"!RPL +!NO CODE +:: + CK1&Dispatch + real + :: + COERCE + CODE + GOSBVL SAVPTR + + ; Load bint in stack level 1 into A[A] + A=DAT1.A + D1=A + D1=D1+5 + A=DAT1.A + + ; Load RBR address into D0 + LC(5) RBR + CD0EX + + ; Write RBR, function code 0; speed is in C[A] + C=A.A + A=0.S + DAT0=A.S + + ; Restore rpl pointers and return to rpl + GOSBVL GETPTR + GOVLNG Loop + + ENDCODE + DROP + ; +; +@" + +END diff --git a/t48.c b/t48.c new file mode 100644 index 0000000..08ae4dc --- /dev/null +++ b/t48.c @@ -0,0 +1,596 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: t48.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: t48.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 17-Feb-1998 +.keywords : * +.description : + This file contains the X Window System interface of the emulator. + +.include : config.h, machdep.h, cpu.h + +.notes : + $Log: t48.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.10 2000/10/24 16:15:00 cibrario + Added/Replaced GPL header + + Revision 1.1 1998/02/19 12:01:36 cibrario + Initial revision + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: t48.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "display.h" +#include "keyb.h" +#include "x11.h" +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID X11_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Chf parameters - Do not change. + The ABNORMAL_EXIT_CODE is taken from stdlib.h (EXIT_FAILURE) + ---------------------------------------------------------------------------*/ + +#define MAIN_MSGCAT_NAME "t48.cat" +#define CONDITION_STACK_SIZE 16 +#define HANDLER_STACK_SIZE 8 +#define ABNORMAL_EXIT_CODE EXIT_FAILURE + + +/*--------------------------------------------------------------------------- + Other parameters + ---------------------------------------------------------------------------*/ + +#define APP_CLASS "T48" +#define LCD_BACKGROUND "green" +#define LCD_FOREGROUND "black" + + +/*--------------------------------------------------------------------------- + Private variables + ---------------------------------------------------------------------------*/ + +static XtAppContext app_context; +static Widget shell_widget; + +/* Command line options descriptor */ +XrmOptionDescRec options[] = +{ + /* option, specifier, argKind, value */ + { "-reset", "*reset", XrmoptionNoArg, "True" }, + { "-monitor", "*monitor", XrmoptionNoArg, "True" }, + { "-path", "*path", XrmoptionSepArg }, + { "-cpu", "*cpu", XrmoptionSepArg }, + { "-mod", "*mod", XrmoptionSepArg }, + { "-hdw", "*hdw", XrmoptionSepArg }, + { "-rom", "*rom", XrmoptionSepArg }, + { "-ram", "*ram", XrmoptionSepArg }, + { "-port1", "*port1", XrmoptionSepArg }, + { "-port2", "*port2", XrmoptionSepArg } +}; + +#define NUM_OPTIONS (sizeof(options)/sizeof(XrmOptionDescRec)) + + +/* Application fallback resources */ +String fallback_resources[] = +{ + NULL /* Null terminated */ +}; + + +/* Application options container */ +struct app_opt +{ + Boolean reset; + Boolean monitor; + Pixel fg_pix; + Pixel bg_pix; + String path; + String cpu; + String mod; + String hdw; + String rom; + String ram; + String port1; + String port2; +}; + + +/* Application resources/options descriptor */ +XtResource app_res[] = +{ + { "foreground", "Foreground", + XtRPixel, sizeof(Pixel), XtOffsetOf(struct app_opt, fg_pix), + XtRString, LCD_FOREGROUND + }, + { "background", "Background", + XtRPixel, sizeof(Pixel), XtOffsetOf(struct app_opt, bg_pix), + XtRString, LCD_BACKGROUND + }, + { "reset", "Reset", + XtRBoolean, sizeof(Boolean), XtOffsetOf(struct app_opt, reset), + XtRString, "False" + }, + { "monitor", "Monitor", + XtRBoolean, sizeof(Boolean), XtOffsetOf(struct app_opt, monitor), + XtRString, "False" + }, + { "path", "Path", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, path), + XtRString, "." + }, + { "cpu", "Cpu", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, cpu), + XtRString, "cpu" + }, + { "mod", "Mod", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, mod), + XtRString, "mod" + }, + { "hdw", "Hdw", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, hdw), + XtRString, "hdw" + }, + { "rom", "Rom", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, rom), + XtRString, "rom" + }, + { "ram", "Ram", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, ram), + XtRString, "ram" + }, + { "port1", "Port1", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, port1), + XtRString, "port1" + }, + { "port2", "Port2", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, port2), + XtRString, "port2" + } +}; + +#define NUM_APP_RES (sizeof(app_res)/sizeof(XtResource)) + + +/*--------------------------------------------------------------------------- + Public variables + ---------------------------------------------------------------------------*/ + +/* Emulator options */ +struct Args args; + + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* KeySym -> HP48 'enum Key' translator */ +static enum Key Ks2K(KeySym ks) +{ + switch(ks) + { + /* Backspace */ + case XK_BackSpace: return KEY_BKSP; + + /* Delete */ + case XK_Delete: return KEY_DEL; + case XK_KP_Delete: return KEY_DEL; + + /* Enter */ + case XK_KP_Enter: return KEY_ENTER; + case XK_Return: return KEY_ENTER; + + /* Cursor keys */ + case XK_KP_Left: return KEY_LEFT; + case XK_Left: return KEY_LEFT; + + case XK_KP_Right: return KEY_RIGHT; + case XK_Right: return KEY_RIGHT; + + case XK_KP_Up: return KEY_UP; + case XK_Up: return KEY_UP; + + case XK_KP_Down: return KEY_DOWN; + case XK_Down: return KEY_DOWN; + + /* Function keys */ + case XK_F1: return KEY_F1; + case XK_F2: return KEY_F2; + case XK_F3: return KEY_F3; + case XK_F4: return KEY_F4; + case XK_F5: return KEY_F5; + case XK_F6: return KEY_F6; + + /* Shift L, R, Alpha, ON */ + case XK_Shift_L: return KEY_SH_L; + case XK_Shift_R: return KEY_SH_R; + case XK_Alt_L: return KEY_ALPHA; + case XK_Alt_R: return KEY_ALPHA; + case XK_Escape: return KEY_ON; + + /* Numeric keypad and surroundings */ + case XK_KP_0: return KEY_0; + case XK_KP_1: return KEY_1; + case XK_KP_2: return KEY_2; + case XK_KP_3: return KEY_3; + case XK_KP_4: return KEY_4; + case XK_KP_5: return KEY_5; + case XK_KP_6: return KEY_6; + case XK_KP_7: return KEY_7; + case XK_KP_8: return KEY_8; + case XK_KP_9: return KEY_9; + + case XK_KP_Decimal: return KEY_DOT; + + case XK_KP_Add: return KEY_ADD; + case XK_plus: return KEY_ADD; + + case XK_KP_Subtract: return KEY_SUB; + case XK_minus: return KEY_SUB; + + case XK_KP_Multiply: return KEY_MUL; + case XK_asterisk: return KEY_MUL; + + case XK_KP_Divide: return KEY_DIV; + case XK_slash: return KEY_DIV; + + case XK_KP_Space: return KEY_SPC; + case XK_space: return KEY_SPC; + + /* Upper half of the keyboard */ + case XK_1: return KEY_MTH; + case XK_2: return KEY_PRG; + case XK_3: return KEY_CST; + case XK_4: return KEY_VAR; + case XK_5: return KEY_UP; + case XK_6: return KEY_NXT; + + case XK_q: return KEY_AP; + case XK_w: return KEY_STO; + case XK_e: return KEY_EVAL; + case XK_r: return KEY_LEFT; + case XK_t: return KEY_DOWN; + case XK_y: return KEY_RIGHT; + + case XK_a: return KEY_SIN; + case XK_s: return KEY_COS; + case XK_d: return KEY_TAN; + case XK_f: return KEY_SQRT; + case XK_g: return KEY_POWER; + case XK_h: return KEY_INV; + + case XK_z: return KEY_ENTER; + case XK_x: return KEY_ENTER; + case XK_c: return KEY_CHS; + case XK_v: return KEY_EEX; + case XK_b: return KEY_DEL; + case XK_n: return KEY_BKSP; + + /* Other useful aliases */ + case XK_Tab: return KEY_NXT; + case XK_apostrophe: return KEY_AP; + case XK_period: return KEY_DOT; + + /* Save */ + case XK_Control_L: + ModSave(); + CpuSave(); + exit(0); + } + + return KEY_NULL; +} + + +/* X Event handler, called by the X Toolkit when appropriate */ +static void XEventHandler(Widget w, XtPointer cl_data, XEvent *ev, + Boolean *cont) +{ + KeySym ks; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "XEventHandler"); + + /* Continue to dispatch */ + *cont = True; + + if(ev->type == Expose) + { + debug1(DEBUG_C_X11, X11_I_LCD_EXPOSE, ev->xexpose.count); + if(ev->xexpose.count == 0) RefreshLcd(); + } + + else if(ev->type == KeyPress || ev->type == KeyRelease) + { + (void)XLookupString((XKeyEvent *)ev, (char *)NULL, 0, &ks, + (XComposeStatus *)NULL); + + if(ev->type == KeyPress) + { + debug1(DEBUG_C_X11, X11_I_KEY_PRESS, ks); + KeybPress(Ks2K(ks)); + } + + else if(ev->type = KeyRelease) + { + debug1(DEBUG_C_X11, X11_I_KEY_RELEASE, ks); + KeybRelease(Ks2K(ks)); + } + } + + else + { + /* Unknown X Event - discard */ + debug1(DEBUG_C_X11, X11_I_X_EVENT, ev->type); + } +} + + +/* Path/name dynamic allocator */ +static char *GetPathname(String path, String name) +{ + char *s = malloc(strlen(path)+strlen(name)+2); + + strcpy(s, path); strcat(s, "/"); strcat(s, name); + return s; +} + + +/* Initialize the X interface */ +void InitializeX(int argc, char *argv[], struct app_opt *opt) +{ + unsigned long fg_pix, bg_pix; + XSetWindowAttributes attr; + Arg xt_args[20]; + int n; + + debug1(DEBUG_C_TRACE, X11_I_CALLED, "InitializeX"); + + /* Setup Arg vector for shell widget creation */ + n = 0; + XtSetArg(xt_args[n], XtNwidth, 131*2+2*8); n++; + XtSetArg(xt_args[n], XtNminWidth, 131*2+2*8); n++; + XtSetArg(xt_args[n], XtNmaxWidth, 131*2+2*8); n++; + XtSetArg(xt_args[n], XtNheight, 64*2+28); n++; + XtSetArg(xt_args[n], XtNminHeight, 64*2+28); n++; + XtSetArg(xt_args[n], XtNmaxHeight, 64*2+28); n++; + XtSetArg(xt_args[n], XtNx, 0); n++; + XtSetArg(xt_args[n], XtNy, 0); n++; + XtSetArg(xt_args[n], XtNinput, True); n++; + + /* Initialize application, parse command line options, + create its main shell. + */ + shell_widget = XtAppInitialize( + &app_context, + APP_CLASS, + options, NUM_OPTIONS, + &argc, argv, + fallback_resources, + xt_args, n + ); + + /* Check unknown options - argv[0] always contains program name */ + if(argc > 1) + { + int i; + for(i=1; i @var{pseudo-terminal} +@end example + +and then the @code{xmodem} send command on the calculator: + +@example +'@var{object_name}' XSEND +@end example + +To transfer a disk file into the calculator, issue the @code{xmodem} +receive command on the calculator: + +@example +'@var{object_name}' XRECV +@end example + +and then the @code{xmodem} send command on the host: + +@example +sx @var{file_name} < @var{pseudo-terminal} > @var{pseudo-terminal} +@end example + +As before, you need to replace @var{pseudo-terminal} with the +name of the emulator's pseudo-terminal in all examples above; +@var{file_name} represents the name of the host file, and +@var{object_name} represents the name of the calculator's object. + + +@node File Transfer with InstantLoad, , File Transfer with Kermit or Xmodem, Using the Emulator +@section File Transfer with InstantLoad +@cindex File Transfer with InstantLoad + +Once you have transferred the @code{sutil} library into the emulated +calculator with a kermit or xmodem file transfer, you can use the +@code{kget} and @code{send} commands provided by this library to +perform very fast transfers directly to/from the calculator's memory. + +The @code{sutil} library is available for some calculator models only; +see @ref{The sutil Library}, for more information. diff --git a/util.msf b/util.msf new file mode 100644 index 0000000..8e21129 --- /dev/null +++ b/util.msf @@ -0,0 +1,38 @@ +$ .+ +$ .identifier : $Id: util.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HPxx emulator +$ .title : $RCSfile: util.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 2-Oct-2000 +$ .keywords : * +$ .description : +$ Message catalog source file for the utility programs. +$ .notes : +$ . $Log: util.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.8 2000/10/19 15:01:43 cibrario +$ . Bug fix: +$ . Removed lines with empty directives +$ . +$ Revision 3.6 2000/10/02 13:59:02 cibrario +$ *** empty log message *** +$ .- + +$set 1 +17 Utilities + +$set 17 +1 Usage:\n\ + pack + +2 Command line syntax error +3 stat(%s) failed +4 Invalid source file size: %d +5 malloc(%d) failed +6 open(%s) failed +7 read(%s) failed +8 WriteNibblesToFile() failed diff --git a/x11.c b/x11.c new file mode 100644 index 0000000..11f1fd1 --- /dev/null +++ b/x11.c @@ -0,0 +1,1974 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: x11.c,v 4.1.1.1 2002/11/11 16:11:47 cibrario Exp $ +.context : SATURN, Saturn CPU / HP48 emulator +.title : $RCSfile: x11.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 5-Sep-2000 +.keywords : * +.description : + This file contains the X Window System interface of the emulator. + + It has been designed and implemented from scratch in less than three days; + this means that it is far from being nice/clean/whatever, and has been + thrown in with the sole purpose of giving a friendlier and somewhat + customizable interface to the Saturn emulator, and to prepare for further + improvments. + + Widget/resource naming conventions + + reset Reset machine at startup + monitor Enter monitor at startup + stateDir Machine state directory + cpu Machine state file names (relative to stateDir, above) + mod + hdw + rom + ram + port1 + port2 + hw Select hardware configuration + face Select machine faceplace and keyboard layout + + main. XmRowColumn container + main..nKeys Number of keys for the given face + main..kbd XmForm container of individual keys + main..kbd. XmForm for key #n, 0 <= n < nKeys + main..kbd..ul Upper left XmLabel of key #n + main..kbd..ur Upper right XmLabel of key #n + main..kbd..ll Lower left XmLabel of key #n + main..kbd..lr Lower right XmLabel of key #n + main..kbd..btn XmToggleButton of key #n + main..kbd..btn.inOut IN/OUT codes of key #n as a string + main..frame XmFrame for LCD screen display + main..frame.lcd XmDrawingArea for LCD screen display + + For all XmLabel and XmToggleButton widgets associated with the emulated + keyboard, the application's resource: + + .compoundString + + can be set to override the labelString String resource with a XmString; + this is necessary, for example, to let a label use mutiple fonts. + The function CheckCompoundString() below does the translation. + +.include : config.h, machdep.h, cpu.h, x11.h + +.notes : + $Log: x11.c,v $ + Revision 4.1.1.1 2002/11/11 16:11:47 cibrario + Small screen support; preliminary + + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.17 2000/11/23 17:03:01 cibrario + Implemented sutil library and assorted bug fixes: + - main GUI components must be reset in ErrorDialogHandler() + + Revision 3.16 2000/11/21 16:43:04 cibrario + Ultrix/IRIX support: + - Removed XmCR_APPLY callback from File Selection Box + + Revision 3.15 2000/11/15 14:13:57 cibrario + GUI enhancements and assorted bug fixes: + + - Added initialization of information/error popup dialog box and + of message TextField in main window. + + - Added handling of command-line option batchXfer and associated + top-level application resource + + - Added Chf condition handler and related installation procedure + to redirect to a GUI element, a popup dialog box, all non-fatal + messages + + - All popup shells now have allowShellResize set to True (to allow + them to accommodate children's size changes) and deleteResponse + set to XmDO_NOTHING (to prevent their untimely destruction when + WM_CLOSE is invoked on them) + + - Added a sanity check to abort the application if the active + faceplate has no keys + + Revision 3.14 2000/11/13 10:44:48 cibrario + Implemented fast load/save; improved keyboard interface emulation at + high emulated CPU speed: + + - In ActivateFSB(), force a search action in the file selection box and + ensure that the default file name is properly qualified. This ensures + that all files show up, including those created after the last + activation, and prevents ambiguity when the application's current + directory is not the same as the fsb's one. + + Revision 3.13 2000/11/09 11:37:09 cibrario + Revised to add file selection box GUI element, CPU halt/run + requests and emulator's extended functions: + + - Implemented new functions: + fsbButtonPressed() (Xt Callback), + InitializeFSB() (initialization function, invoked by InitializeGui()), + ResetToggleButtons() (main GUI reset function) + ActivateFSB() (public function to activate the file selection box) + + Revision 3.10 2000/10/24 16:15:03 cibrario + Added/Replaced GPL header + + Revision 3.5 2000/10/02 09:53:30 cibrario + Linux support: + - added an explicit exit() invocation in deleteWindow(); the default + Motif application's destructor is not invoked with Debian + lesstif 0.89.4-3 when deleteWindow() is registered. + + Revision 3.1 2000/09/20 14:08:25 cibrario + Revised to implement passive CPU shutdown: + - implemented IdleXLoop() function and its ancillary Xt timeout + handler, IdleTimeOutHandler() + + * Revision 2.8 2000/09/19 12:55:18 cibrario + * The translation table of ToggleButtons is no longer set during + * widget creation, to be able to install default keyboard + * translations, via app-resource file, to buttons too. + * Instead, XtAugmentTranslations() is used after widget creation. + * + * Revision 2.3 2000/09/12 12:30:42 cibrario + * Bug fix: signalling of X11_I_REVISION (X11_RCS_INFO) was missing + * in InitializeGui(). + * + * Revision 2.2 2000/09/11 14:02:17 cibrario + * Bug fix: removed spurious printf() from InitializeWidgets + * + * Revision 2.1 2000/09/08 15:36:33 cibrario + * *** empty log message *** + * + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: x11.c,v 4.1.1.1 2002/11/11 16:11:47 cibrario Exp $"; +#endif + +#include +#include +#include +#include + +#include /* Main X header */ + +#include /* Main Xt header */ +#include /* Literal strings of widget attributes */ + +#include /* Main OSF/Motif header */ +#include /* XmMainWindow widget */ +#include /* XmForm widget */ +#include /* XmLabel widget */ +#include /* XmToggleButton widget */ +#include /* XmRowColumn widget */ +#include /* XmFrame widget */ +#include /* XmDrawingArea widget */ +#include /* XmFileSelectionBox widget */ +#include /* XmMessageBox widget */ +#include /* XmTextField widget */ +#include /* XmAddWMProtocolCallback() */ + +#include "config.h" +#include "machdep.h" +#include "cpu.h" /* EmulatorExit(); required by keyb.h, too */ +#include "display.h" /* LcdInit() */ +#include "keyb.h" +#include "serial.h" +#include "x11.h" +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID X11_CHF_MODULE_ID +#include + + +/*--------------------------------------------------------------------------- + Misc. parameters + ---------------------------------------------------------------------------*/ + +#define APP_CLASS "Saturn" +#define MAX_CS_SEGMENT_LEN 80 + + +/*--------------------------------------------------------------------------- + Private variables and type definitions + ---------------------------------------------------------------------------*/ + +static XtAppContext app_context; +static Widget shell_widget; +static Widget file_sel_box; +static Widget error_dialog; +static Widget msg_text_field; + +#define MAX_ERROR_DIALOG_COUNT 10 +static int error_dialog_count; + +/* Forward static function declarations */ +static void ResetToggleButtons(Widget w); + + +/* Continuation procedure to invoke when a FSB button is pressed */ +static FsbContinuation fsb_cont = (FsbContinuation)NULL; + + +/* Command line option descriptors; these descriptors map command line + options into X resource settings +*/ +static XrmOptionDescRec options[] = +{ + /* option, specifier, argKind, value */ + { "-reset", "*reset", XrmoptionNoArg, "True" }, + { "-monitor", "*monitor", XrmoptionNoArg, "True" }, + { "-batchXfer", "*batchXfer", XrmoptionNoArg, "True" }, + { "-stateDir", "*stateDir", XrmoptionSepArg }, + { "-cpu", "*cpu", XrmoptionSepArg }, + { "-mod", "*mod", XrmoptionSepArg }, + { "-hdw", "*hdw", XrmoptionSepArg }, + { "-rom", "*rom", XrmoptionSepArg }, + { "-ram", "*ram", XrmoptionSepArg }, + { "-port1", "*port1", XrmoptionSepArg }, + { "-port2", "*port2", XrmoptionSepArg }, + { "-face", "*face", XrmoptionSepArg }, + { "-hw", "*hw", XrmoptionSepArg } +}; + +#define NUM_OPTIONS XtNumber(options) + + +/* Application fallback resources */ +static String fallback_resources[] = +{ + NULL /* Null terminated */ +}; + + +/* Application resources container (top-level resources) */ +struct app_opt +{ + Boolean reset; + Boolean monitor; + Boolean batchXfer; + String stateDir; + String cpu; + String mod; + String hdw; + String rom; + String ram; + String port1; + String port2; + String face; + String hw; +}; + + +/* Application resources container (per-face resources) */ +struct face_opt +{ + int nKeys; +}; + + +/* Application resources container (per-button resources) */ +struct btn_opt +{ + String inOut; +}; + + +/* Application resources container (compoundString resource) */ +struct cs_opt +{ + String compoundString; +}; + + +/* Application resource descriptors (top-level resources) */ +static XtResource app_res[] = +{ + { "reset", "Reset", + XtRBoolean, sizeof(Boolean), XtOffsetOf(struct app_opt, reset), + XtRString, "False" + }, + { "monitor", "Monitor", + XtRBoolean, sizeof(Boolean), XtOffsetOf(struct app_opt, monitor), + XtRString, "False" + }, + { "batchXfer", "BatchXfer", + XtRBoolean, sizeof(Boolean), XtOffsetOf(struct app_opt, batchXfer), + XtRString, "False" + }, + { "stateDir", "StateDir", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, stateDir), + XtRString, "." + }, + { "cpu", "Cpu", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, cpu), + XtRString, "cpu" + }, + { "mod", "Mod", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, mod), + XtRString, "mod" + }, + { "hdw", "Hdw", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, hdw), + XtRString, "hdw" + }, + { "rom", "Rom", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, rom), + XtRString, "rom" + }, + { "ram", "Ram", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, ram), + XtRString, "ram" + }, + { "port1", "Port1", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, port1), + XtRString, "port1" + }, + { "port2", "Port2", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, port2), + XtRString, "port2" + }, + { "face", "Face", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, face), + XtRString, "hp48" + }, + { "hw", "Hw", + XtRString, sizeof(String), XtOffsetOf(struct app_opt, hw), + XtRString, "hp48" + } +}; + +#define NUM_APP_RES XtNumber(app_res) + + +/* Application resource descriptors (per-face resources) */ +static XtResource face_res[] = +{ + { "nKeys", "NKeys", + XtRInt, sizeof(int), XtOffsetOf(struct face_opt, nKeys), + XtRString, "0" /* Face has no keys by default */ + } +}; + +#define NUM_FACE_RES XtNumber(face_res) + + +/* Application resource descriptors (per-button resources) */ +static XtResource btn_res[] = +{ + { "inOut", "InOut", + XtRString, sizeof(String), XtOffsetOf(struct btn_opt, inOut), + XtRString, "00/0" /* Do-nothing inOut */ + } +}; + +#define NUM_BTN_RES XtNumber(btn_res) + + +/* Application resource descriptors (compoundString resource) */ +static XtResource cs_res[] = +{ + { "compoundString", "CompoundString", + XtRString, sizeof(String), XtOffsetOf(struct cs_opt, compoundString), + XtRString, NULL /* NULL string by default */ + } +}; + +#define NUM_CS_RES XtNumber(cs_res) + + +/* Xt Actions */ + +static void kbdKeyPress(Widget w, XEvent *xe, String *argv, Cardinal *argc); +static void kbdKeyRelease(Widget w, XEvent *xe, String *argv, Cardinal *argc); + +static XtActionsRec xt_actions[] = +{ + { "kbdKeyPress", kbdKeyPress }, + { "kbdKeyRelease", kbdKeyRelease } +}; + +#define NUM_XT_ACTIONS XtNumber(xt_actions) + + +/*--------------------------------------------------------------------------- + Public variables + ---------------------------------------------------------------------------*/ + +/* Emulator options; they are initialized here from X resources */ +struct Args args; + + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* Path/name dynamic allocator */ +static char *GetPathname(String path, String name) +{ + char *s = malloc(strlen(path)+strlen(name)+2); + + strcpy(s, path); strcat(s, "/"); strcat(s, name); + return s; +} + + +/*--------------------------------------------------------------------------- + Private functions: Xt Actions and Callbacks + ---------------------------------------------------------------------------*/ + +#ifdef LONG_PRESS_THR +/* 4.1.1.1: This variable saves the latest mouse button arm time; it will be + used at button release, to compute how much time the button has been kept + pressed. Notice that this is a bit simplistic, since it assumes that only + one button is pressed at a time. +*/ +static Time arm_time = CurrentTime; + +/* Compute the difference between two X Server timestamps, taking wrap around + of a CARD32 into account. +*/ +#define TimeDiff(n, o) (((n) - (o)) & 0xFFFFFFFFUL) +#endif + + +/* This function is called when a keyboard button is armed */ +static void kbdButtonArmed( + Widget w, XtPointer client_data, XtPointer call_data) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "kbdButtonArmed"); + +#ifdef LONG_PRESS_THR + /* Save arm_time for use at release */ + arm_time = ((XmToggleButtonCallbackStruct *)call_data) + ->event->xbutton.time; +#endif + + KeybPress((char *)client_data); +} + + +/* This function is called when a keyboard button is disarmed */ +static void kbdButtonDisarmed( + Widget w, XtPointer client_data, XtPointer call_data) +{ + XmToggleButtonCallbackStruct *info = + (XmToggleButtonCallbackStruct *)call_data; + + XEvent *event = info->event; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "kbdButtonDisarmed"); + +#ifdef LONG_PRESS_THR + if(TimeDiff(event->xbutton.time, arm_time) > LONG_PRESS_THR) + { + /* Button pressed for more than LONG_PRESS_THR; keep it pressed */ + XmToggleButtonSetState( + w, + True, + False + ); + } + + else +#endif + + if(event->type == ButtonRelease + && event->xbutton.button == 3) + { + /* Keep the button pressed */ + XmToggleButtonSetState( + w, + True, + False + ); + } + else + { + /* Release the button */ + XmToggleButtonSetState( + w, + False, + False + ); + + KeybRelease((char *)client_data); + } +} + + +/* This function is called when the lcd widget receives an Expose event */ +static void lcdExposed( + Widget w, XtPointer client_data, XtPointer call_data) +{ + XmDrawingAreaCallbackStruct *expose_data = + (XmDrawingAreaCallbackStruct *)call_data; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "lcdExposed"); + + if(expose_data->event->type == Expose + && expose_data->event->xexpose.count == 0) + RefreshLcd(); +} + + +/* This function is called when the main shell window is about to be + destroyed by the wm +*/ +static void deleteWindow( + Widget w, XtPointer client_data, XtPointer call_data) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "deleteWindow"); + + /* 3.5: Added linux support. + + Attempt to save the emulator state on mass storage; default + Motif callback should actually destroy the GUI and exit the + application. + + However, this does not work with Debian lesstif 0.89.4-3, + so an explicit exit() has been added here; it should not + harm other architectures. + */ + EmulatorExit(SAVE_AND_EXIT); + exit(EXIT_SUCCESS); +} + + +/* This function is called when the session manager wants the + application to save its state. This procedure should update the + WM_COMMAND property of the shell window when done. +*/ +static void saveYourself( + Widget w, XtPointer client_data, XtPointer call_data) +{ + int argc; /* Argument vector to/from WM_COMMAND property */ + char **argv; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "saveYourself"); + + /* Attempt to save the emulator state on mass storage; default + Motif callback will actually destroy the GUI and exit the + application. + */ + EmulatorExit(SAVE_AND_EXIT); + + /* Update the WM_COMMAND property on w, and let the session manager + continue its work. + */ + if(!XGetCommand(XtDisplay(w), XtWindow(w), &argv, &argc)) + { + /* Property not found / wrong format / ... */ + ChfCondition X11_E_NO_WM_COMMAND, CHF_ERROR ChfEnd; + ChfSignal(); + } + else + { + /* Set the property to the same value it had and free the string + list returned by Xlib. + */ + XSetCommand(XtDisplay(w), XtWindow(w), argv, argc); + XFreeStringList(argv); + } +} + + +/* This function is called when a shortcut key is pressed */ +static void kbdKeyPress(Widget w, XEvent *xe, String *argv, Cardinal *argc) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "kbdKeyPress"); + + if(*argc == 1) + { + debug1(DEBUG_C_X11, X11_I_KEY_PRESS, argv[0]); + KeybPress(argv[0]); + } + else + { + ChfCondition X11_W_BAD_ACTION_CALL, CHF_WARNING, argc ChfEnd; + ChfSignal(); + } +} + + +/* This function is called when a shortcut key is released */ +static void kbdKeyRelease(Widget w, XEvent *xe, String *argv, Cardinal *argc) +{ + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "kbdKeyRelease"); + + if(*argc == 1) + { + debug1(DEBUG_C_X11, X11_I_KEY_RELEASE, argv[0]); + KeybRelease(argv[0]); + } + else + { + ChfCondition X11_W_BAD_ACTION_CALL, CHF_WARNING, argc ChfEnd; + ChfSignal(); + } +} + + +/* This function is called when a button of the FileSelectionBox + is activated. +*/ +static void fsbButtonPressed( + Widget w, + XtPointer client_data, XtPointer call_data) +{ + XmFileSelectionBoxCallbackStruct *fsb_data = + (XmFileSelectionBoxCallbackStruct *)call_data; + + /* Continuation must proceed only if OK was activated. + + 3.16: XmCR_APPLY must not be handled here, because it corresponds + to the 'Filter' key. + */ + int proceed = + (fsb_data->reason == XmCR_OK); + + char *value = (char *)NULL; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "fsbButtonPressed"); + + if(fsb_cont != (FsbContinuation)NULL) + { + /* If continuation must proceed, get the current user selection + from the file selection box and convert it into a char *; + the conversion should never fail. + */ + if(proceed && + !XmStringGetLtoR(fsb_data->value, XmFONTLIST_DEFAULT_TAG, &value)) + { + ChfCondition X11_E_NO_FSB_TSEG, CHF_ERROR ChfEnd; + ChfSignal(); + } + + else + { + /* Invoke continuation; value is meaningful only when + proceed is true. + */ + fsb_cont(proceed, value); + + /* Free the dynamically-allocated user selection, if any. + XtFree() handles NULL pointers correctly. + */ + XtFree(value); + } + } + + else + { + /* Continuation not set; do nothing */ + ChfCondition X11_W_NO_FSB_CONT, CHF_WARNING ChfEnd; + ChfSignal(); + } + + /* Remove the popup shell */ + XtUnmanageChild(w); +} + + +/* This utility function builds an XmString from a string, taking newlines + and tabs into account: when a newline is found, the separator 'sep' + is put into the string; when a tab is found, the spacer 'spc' is put + into the string. The function destroys the original string s in the + process. +*/ +static XmString XmStringFromString(char *s, XmString sep, XmString spc) +{ + char *p; /* String scan pointer */ + char c; /* Current String character */ + + XmString x; /* XmString from current segment */ + XmString n; /* Result of concatenation */ + + XmString m = XmStringCreate("", XmFONTLIST_DEFAULT_TAG); /* Buffer */ + + p = s; + while((c = *p) != '\0') + { + if(c == '\n' || c == '\t') + { + /* Terminate current segment */ + *p = '\0'; + + /* Append segment to XmString, if not empty */ + if(p > s) + { + x = XmStringCreate(s, XmFONTLIST_DEFAULT_TAG); + n = XmStringConcat(m, x); + + XmStringFree(m); + XmStringFree(x); + m = n; + } + + /* Append either sep or spc */ + n = XmStringConcat(m, (c == '\n') ? sep : spc); + + XmStringFree(m); + m = n; + + /* Skip current segment and start a new one */ + p++; + s = p; + } + + else + /* Current segment continues */ + p++; + } + + if(p > s) + { + /* Non-empty pending segment; append */ + x = XmStringCreate(s, XmFONTLIST_DEFAULT_TAG); + n = XmStringConcat(m, x); + + XmStringFree(m); + XmStringFree(x); + m = n; + } + + return m; +} + + +/* This is a Chf handler; it is called when any condition is signalled + and prints the message through the error dialog. This handler also + intercepts the SERIAL_CHF_MODULE_ID/SERIAL_I_PTY_NAME message and + puts it into the main message display area. + + When the number of messages in the error dialog exceeds + MAX_ERROR_DIALOG_COUNT, further messages are immediately + resignaled; also, condition X11_W_TOO_MANY_MSG is signaled once. +*/ +static ChfAction ErrorDialogHandler( + const ChfDescriptor *d, + const ChfState s, + ChfPointer ctx) +{ + ChfAction act; + + /* Check Chf state */ + switch(s) + { + + case CHF_SIGNALING: + /* ChfSignal() in progress */ + if(ChfGetSeverity(d) == CHF_FATAL) + { + /* Severity is FATAL; the application is exiting. + No point to use a message window; resignal now. + */ + act = CHF_RESIGNAL; + } + + else if(ChfGetModuleId(d) == SERIAL_CHF_MODULE_ID + && ChfGetConditionCode(d) == SERIAL_I_PTY_NAME) + { + /* Pseudo-terminal message; this is very important. + Put it into the message display area. + + This is also an example of how you can intercept a + condition message and do anything with it *without* + changing a line of code elsewhere and, in particular, + in the place where the condition is generated. + */ + Arg xt_args[20]; + int n; + + n = 0; + XtSetArg(xt_args[n], XmNvalue, ChfGetPartialMessage(d)); n++; + XtSetValues(msg_text_field, xt_args, n); + + act = CHF_CONTINUE; + } + + else + { + /* If maximum value of error_dialog_count has been reached, + resignal. + */ + if(error_dialog_count++ == MAX_ERROR_DIALOG_COUNT) + { + ChfCondition X11_W_TOO_MANY_MSG, CHF_WARNING, + MAX_ERROR_DIALOG_COUNT ChfEnd; + ChfSignal(); + + act = CHF_RESIGNAL; + } + + else if(error_dialog_count > MAX_ERROR_DIALOG_COUNT) + act = CHF_RESIGNAL; + + else + { + unsigned char dialog_type = XmDIALOG_INFORMATION; + XmString sep = XmStringSeparatorCreate(); + XmString spc = XmStringCreate(" ", XmFONTLIST_DEFAULT_TAG); + XmString m, c, o; + + Arg xt_args[20]; + int n; + + /* 3.17: Reset GUI: keys and (recursively) buttons; + see comment in ActivateFSB() for more information. + */ + KeybReset(); + ResetToggleButtons(shell_widget); + + /* Determine dialog_type from top condition's severity */ + if(ChfGetSeverity(d) == CHF_ERROR) + dialog_type = XmDIALOG_ERROR; + else if(ChfGetSeverity(d) == CHF_WARNING) + dialog_type = XmDIALOG_WARNING; + + /* Put the message into the dialog; be careful with newlines */ + m = XmStringFromString(ChfBuildMessage(d), sep, spc); + + while((d = ChfGetNextDescriptor(d)) != CHF_NULL_DESCRIPTOR) + { + o = XmStringFromString(ChfBuildMessage(d), sep, spc); + c = XmStringConcat(m, o); XmStringFree(m); m = c; + XmStringFree(o); + } + + /* Get old value of messageString */ + n = 0; + XtSetArg(xt_args[n], XmNmessageString, &o); n++; + XtGetValues(error_dialog, xt_args, n); + + /* Append a separator to old messageString */ + c = XmStringConcat(o, sep); XmStringFree(o); o = c; + + /* Append new string to the old one */ + c = XmStringConcat(o, m); XmStringFree(o); o = c; + + /* Set the error dialog resources */ + n = 0; + XtSetArg(xt_args[n], XmNdialogType, dialog_type); n++; + XtSetArg(xt_args[n], XmNmessageString, o); n++; + XtSetValues(error_dialog, xt_args, n); + + /* Free XmStrings */ + XmStringFree(o); + XmStringFree(m); + XmStringFree(sep); + XmStringFree(spc); + + /* Display the error dialog */ + XtManageChild(error_dialog); + + act = CHF_CONTINUE; + } + } + break; + + default: + /* Other states; resignal the condition */ + act = CHF_RESIGNAL; + break; + } + + return act; +} + + +/* This callback is invoked when the OK button of the error dialog + is pressed. +*/ +static void errorButtonPressed( + Widget w, + XtPointer client_data, XtPointer call_data) +{ + XmString e = XmStringCreate("", XmFONTLIST_DEFAULT_TAG); + + Arg xt_args[20]; + int n; + + /* Clear messageString */ + n = 0; + XtSetArg(xt_args[n], XmNmessageString, e); n++; + XtSetValues(error_dialog, xt_args, n); + + XmStringFree(e); + + /* Reset message counter */ + error_dialog_count = 0; + + /* Remove the popup shell */ + XtUnmanageChild(w); +} + + +/*--------------------------------------------------------------------------- + Private functions: Initialization + ---------------------------------------------------------------------------*/ + +/* This function initializes the Xt application context and + gets the application resources. Some Xt variables (app_context and + the main shell widget) are static. + Instead, the application's options structure (opt) is passed by + reference as an argument. +*/ +static void InitializeXt(int argc, char *argv[], struct app_opt *opt) +{ + Atom a; + + Arg xt_args[20]; + int n; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "InitializeXt"); + + /* Enable default Xt language setup procedure */ + XtSetLanguageProc(NULL, NULL, NULL); + + /* Setup Arg vector for shell widget creation */ + n = 0; + XtSetArg(xt_args[n], XmNallowShellResize, True); n++; + + /* Initialize application, parse command line options, + create its main shell. + */ + shell_widget = XtAppInitialize( + &app_context, + APP_CLASS, + options, NUM_OPTIONS, + &argc, argv, + fallback_resources, + xt_args, n + ); + + /* Add WMProtocolCallback for WM_DELETE_WINDOW and WM_SAVE_YOURSELF */ + if((a = XmInternAtom(XtDisplay(shell_widget), "WM_DELETE_WINDOW", True)) + != None) + /* XmAddWMProtocolCallback() invokes XmAddProtocolCallback(); + in turn, XmAddProtocolCallback() automatically calls + XmAddProtocols() if the protocol has not yet been registered. + Therefore, XmAddWMProtocols(shell_widget, &a, 1) is not needed here. + */ + XmAddWMProtocolCallback( + shell_widget, a, deleteWindow, (XtPointer)NULL); + else + { + ChfCondition X11_W_UNKNOWN_ATOM, CHF_WARNING, "WM_DELETE_WINDOW" + ChfEnd; + ChfSignal(); + } + + if((a = XmInternAtom(XtDisplay(shell_widget), "WM_SAVE_YOURSELF", True)) + != None) + XmAddWMProtocolCallback( + shell_widget, a, saveYourself, (XtPointer)NULL); + else + { + ChfCondition X11_W_UNKNOWN_ATOM, CHF_WARNING, "WM_SAVE_YOURSELF" + ChfEnd; + ChfSignal(); + } + + /* Spot unknown options - argv[0] always contains program name */ + if(argc > 1) + { + int i; + for(i=1; iface); + + /* Install Xt actions */ + XtAppAddActions( + app_context, + xt_actions, NUM_XT_ACTIONS + ); +} + + +/* Initialize the fast-load popup window */ +static void InitializeFSB(void) +{ + Arg xt_args[20]; + int n; + + /* Setup Arg vector for file selection dialog & box, and create them. + It's not necessary to set XmNscreen here, because we want the + default screen and this is dynamically set in shell_widget. + + 3.15: Set allowShellResize to True; this way the shell will be + able to accommodate children's size changes. Also set + deleteResponse to XmDO_NOTHING; this prevents dangerous implicit + destruction of the shell widget when the wm allows direct WM_CLOSE + actions on it. + */ + n = 0; + XtSetArg(xt_args[n], XmNallowShellResize, True); n++; + XtSetArg(xt_args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; + XtSetArg(xt_args[n], XmNdeleteResponse, XmDO_NOTHING); n++; + + file_sel_box = XmCreateFileSelectionDialog( + shell_widget, + "fsb", + xt_args, n + ); + + /* Make unused buttons insensitive */ + XtSetSensitive( + XmFileSelectionBoxGetChild(file_sel_box, XmDIALOG_HELP_BUTTON), + False); + + /* Add callbacks for relevant buttons. + + 3.16: XmNapplyCallback corresponds to the 'Filter' key in this case, + and must not be handled directly by the application. + */ + XtAddCallback( + file_sel_box, + XmNokCallback, + fsbButtonPressed, + NULL + ); + + XtAddCallback( + file_sel_box, + XmNcancelCallback, + fsbButtonPressed, + NULL + ); +} + + +/* Initialize the dialog used to display error messages, and install + a Chf handler to use it. +*/ +static void InitializeErrorDialog(void) +{ + Arg xt_args[20]; + int n; + + /* Setup Arg vector for error dialog and create it. + It's not necessary to set XmNscreen here, because we want the + default screen and this is dynamically set in shell_widget. + + Set allowShellResize to True; this way the shell will be + able to accommodate children's size changes. Also set + deleteResponse to XmDO_NOTHING; this prevents dangerous implicit + destruction of the shell widget when the wm allows direct WM_CLOSE + actions on it. + */ + n = 0; + XtSetArg(xt_args[n], XmNallowShellResize, True); n++; + XtSetArg(xt_args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; + XtSetArg(xt_args[n], XmNdeleteResponse, XmDO_NOTHING); n++; + + error_dialog = XmCreateErrorDialog( + shell_widget, + "error", + xt_args, n + ); + + /* Make unused buttons insensitive */ + XtSetSensitive( + XmMessageBoxGetChild(error_dialog, XmDIALOG_HELP_BUTTON), + False); + + XtSetSensitive( + XmMessageBoxGetChild(error_dialog, XmDIALOG_CANCEL_BUTTON), + False); + + /* Add callback for the OK button */ + XtAddCallback( + error_dialog, + XmNokCallback, + errorButtonPressed, + NULL + ); + + /* Install a Chf handler for it */ + ChfPushHandler(ErrorDialogHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER); + + /* Reset message count */ + error_dialog_count = 0; +} + + +/* This function checks if widget w has a compoundString application's + resource set. If yes, the resource is parsed and the labelString + widget resource is set accordingly. compoundString syntax: + + : ([# ] ])* + : + # -- Put a single '#' in current segment + -- Create a new segment using XmFONTLIST_DEFAULT_TAG as a tag + -- Create a new segment using as a tag + + Each segment is limited to MAX_CS_SEGMENT_LEN characters; longer + segments are silently truncated. + + Too lazy to build a full-fledged resource translator... +*/ +static void CheckCompoundString(Widget w) +{ + struct cs_opt opt; + + Arg xt_args[10]; + int n; + + XtGetApplicationResources( + w, + (XtPointer)&opt, + cs_res, + NUM_CS_RES, + (ArgList)NULL, + (Cardinal)0 + ); + + if(opt.compoundString != NULL) + { + char user_tag[2]; /* User tag buffer, 1 char */ + char seg_buf[MAX_CS_SEGMENT_LEN+1]; /* Segment buffer */ + char *cur_tag = XmFONTLIST_DEFAULT_TAG; /* Tag of current segment */ + char *cur_sptr = opt.compoundString; /* Parser's source ptr */ + char *cur_dptr = seg_buf; /* Dest ptr in seg_buf */ + char cur_c; /* Current source char */ + char next_c; /* Next source char */ + XmString xm_string; /* Parsed xm_string */ + XmString cur_xm; /* Parsed segment */ + XmString new_xm; + + debug2(DEBUG_C_X11, X11_I_FOUND_CS, + XtName(XtParent(w)), opt.compoundString); + + user_tag[1] = '\0'; + xm_string = XmStringCreate("", XmFONTLIST_DEFAULT_TAG); + + /* Parse the compoundString */ + while((cur_c = *cur_sptr++) != '\0') + { + if(cur_c == '#') + { + /* Tag marker */ + if((next_c = *cur_sptr) == '\0') + { + /* Syntax error; ignore trailing # */ + } + else + { + if(next_c == '#') + { + /* Escaped #; store in current segment and skip */ + if(cur_dptr - seg_buf < MAX_CS_SEGMENT_LEN) + *cur_dptr++ = next_c; + } + else + { + /* Close current segment and append to xm_string */ + if(cur_dptr - seg_buf > 0) + { + *cur_dptr = '\0'; + cur_xm = XmStringCreate(seg_buf, cur_tag); + new_xm = XmStringConcat(xm_string, cur_xm); + XmStringFree(xm_string); + XmStringFree(cur_xm); + xm_string = new_xm; + cur_dptr = seg_buf; + } + + /* Set new current tag */ + if(next_c == ' ') + cur_tag = XmFONTLIST_DEFAULT_TAG; + else + { + user_tag[0] = next_c; + cur_tag = user_tag; + } + } + + cur_sptr++; + } + } + else + { + /* Store char in current segment */ + if(cur_dptr - seg_buf < MAX_CS_SEGMENT_LEN) + *cur_dptr++ = cur_c; + } + } + + /* Close ending segment and append to xm_string */ + if(cur_dptr - seg_buf > 0) + { + *cur_dptr = '\0'; + cur_xm = XmStringCreate(seg_buf, cur_tag); + new_xm = XmStringConcat(xm_string, cur_xm); + XmStringFree(xm_string); + XmStringFree(cur_xm); + xm_string = new_xm; + } + + /* Pant... now push xm_string into the widget */ + n = 0; + XtSetArg(xt_args[n], XmNlabelString, xm_string); n++; + XtSetValues(w, xt_args, n); + XmStringFree(xm_string); + } +} + + +/* This function creates the widget set corresponding to a key, and + returns the widget id of the top widget +*/ +static Widget CreateKey(int k, Widget parent) +{ + char container_name[8]; + Widget w, l, b; + XtTranslations t; + struct btn_opt opt; + + Arg xt_args[20]; + int n; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "CreateKey"); + + /* Generate container_name */ + sprintf(container_name, "%d", k); + + /* Container widget (xmForm) */ + /* Force nonmodal navigation only; don't allow overlapping children */ + n = 0; +#ifdef FORCE_NONMODAL + XtSetArg(xt_args[n], XmNnavigationType, XmNONE); n++; + XtSetArg(xt_args[n], XmNtraversalOn, False); n++; +#endif + XtSetArg(xt_args[n], XmNallowOverlap, False); n++; + + w = XtCreateManagedWidget( + container_name, + xmFormWidgetClass, + parent, + xt_args, n + ); + + /* Upper labels */ + /* Must be widgets to set own foreground color */ + n = 0; + XtSetArg(xt_args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(xt_args[n], XmNtopAttachment, XmATTACH_FORM); n++; + + l = XtCreateManagedWidget( + "ul", + xmLabelWidgetClass, + w, + xt_args, n + ); + + CheckCompoundString(l); + + n = 0; + XtSetArg(xt_args[n], XmNalignment, XmALIGNMENT_END); n++; + XtSetArg(xt_args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; + XtSetArg(xt_args[n], XmNleftWidget, l); n++; + XtSetArg(xt_args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(xt_args[n], XmNtopAttachment, XmATTACH_FORM); n++; + + CheckCompoundString( + XtCreateManagedWidget( + "ur", + xmLabelWidgetClass, + w, + xt_args, n + )); + + /* ToggleButton */ + /* - Disable ToggleButton indicator; shadowThickness must be >0 to + enable 3D shadowing + + 2.8: The translation table of the ToggleButton is no longer set + here, to be able to install default keyboard translations, + via app-resource file, to buttons too. + Instead, XtAugmentTranslations() is used after widget creation. + */ + n = 0; + XtSetArg(xt_args[n], XmNindicatorOn, False); n++; + XtSetArg(xt_args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(xt_args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(xt_args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; + XtSetArg(xt_args[n], XmNtopWidget, l); n++; + + b = XtCreateManagedWidget( + "btn", + xmToggleButtonWidgetClass, + w, + xt_args, n + ); + + /* Augment the translation table to allow clicks with button 3; + this key is used to hold a key pressed. + */ + XtAugmentTranslations( + b, + (t = XtParseTranslationTable( + "#augment : Arm()\n" + ": Select() Disarm()\n"))); + + XtFree((void *)t); /* Free the translation table */ + CheckCompoundString(b); + + /* Lower labels */ + /* Must be widgets to set own foreground color */ + n = 0; + XtSetArg(xt_args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(xt_args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; + XtSetArg(xt_args[n], XmNtopWidget, b); n++; + + l = XtCreateManagedWidget( + "ll", + xmLabelWidgetClass, + w, + xt_args, n + ); + + CheckCompoundString(l); + + n = 0; + XtSetArg(xt_args[n], XmNalignment, XmALIGNMENT_END); n++; + XtSetArg(xt_args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; + XtSetArg(xt_args[n], XmNleftWidget, l); n++; + XtSetArg(xt_args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(xt_args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; + XtSetArg(xt_args[n], XmNtopWidget, b); n++; + + CheckCompoundString( + XtCreateManagedWidget( + "lr", + xmLabelWidgetClass, + w, + xt_args, n + )); + + /* Get application resources of ToggleButton and fill + the 'struct btn_opt opt' structure appropriately + */ + XtGetApplicationResources( + b, + (XtPointer)&opt, + btn_res, + NUM_BTN_RES, + (ArgList)NULL, + (Cardinal)0 + ); + + debug2(DEBUG_C_X11, X11_I_KEY, k, opt.inOut); + + /* Add Arm/Disarm callbacks on ToggleButton */ + XtAddCallback( + b, + XmNarmCallback, + kbdButtonArmed, + opt.inOut + ); + + XtAddCallback( + b, + XmNdisarmCallback, + kbdButtonDisarmed, + opt.inOut + ); + + return w; +} + + +/* This function creates, initializes and realizes the widget tree. + Initialization includes callback installation. +*/ +static void InitializeWidgets(String face, + Display **lcd_display, Window *lcd_window, + unsigned long *lcd_fg, unsigned long *lcd_bg) +{ + Widget mw, rc, kbd, f, lcd; + int k; + struct face_opt opt; + Pixel lcd_fg_pixel, lcd_bg_pixel; + + Arg xt_args[20]; + int n; + + debug1(DEBUG_C_TRACE|DEBUG_C_X11, X11_I_CALLED, "InitializeWidgets"); + + /* Main window */ + /* Force nonmodal navigation only */ + n = 0; +#ifdef FORCE_NONMODAL + XtSetArg(xt_args[n], XmNnavigationType, XmNONE); n++; + XtSetArg(xt_args[n], XmNtraversalOn, False); n++; +#endif + + mw = XtCreateManagedWidget( + "main", + xmMainWindowWidgetClass, + shell_widget, + xt_args, n + ); + + /* RowColumn container for display, keyboard and message area */ + /* Force nonmodal navigation only */ + n = 0; +#ifdef FORCE_NONMODAL + XtSetArg(xt_args[n], XmNnavigationType, XmNONE); n++; + XtSetArg(xt_args[n], XmNtraversalOn, False); n++; +#endif + + rc = XtCreateManagedWidget( + face, + xmRowColumnWidgetClass, + mw, + xt_args, n + ); + + /* Get .nKeys resource (opt->nKeys) */ + XtGetApplicationResources( + rc, + (XtPointer)&opt, + face_res, + NUM_FACE_RES, + (ArgList)NULL, + (Cardinal)0 + ); + + /* Announce resource value (debug only) */ + debug1(DEBUG_C_X11, X11_I_NKEYS, opt.nKeys); + + /* 3.15: Cannot continue if the active faceplate has no keys; + the application resource file is probably wrong. + */ + if(opt.nKeys <= 0) + { + ChfCondition X11_F_NO_KEYS, CHF_FATAL, face ChfEnd; + ChfSignal(); + } + + /* LCD Screen */ + n = 0; +#ifdef FORCE_NONMODAL + XtSetArg(xt_args[n], XmNnavigationType, XmNONE); n++; + XtSetArg(xt_args[n], XmNtraversalOn, False); n++; +#endif + f = XtCreateManagedWidget( + "frame", + xmFrameWidgetClass, + rc, + xt_args, n + ); + + n = 0; + lcd = XtCreateManagedWidget( + "lcd", + xmDrawingAreaWidgetClass, + f, + xt_args, n + ); + + /* Add expose callback on lcd widget */ + XtAddCallback( + lcd, + XmNexposeCallback, + lcdExposed, + (XtPointer)NULL + ); + + /* Keyboard form */ + /* Force nonmodal navigation only */ + n = 0; +#ifdef FORCE_NONMODAL + XtSetArg(xt_args[n], XmNnavigationType, XmNONE); n++; + XtSetArg(xt_args[n], XmNtraversalOn, False); n++; +#endif + XtSetArg(xt_args[n], XmNallowOverlap, False); n++; + + kbd = XtCreateManagedWidget( + "kbd", + xmFormWidgetClass, /* widget_class */ + rc, + xt_args, n + ); + + /* Create keys */ + for(k=0; k]\n\t\t[-face <>] [-hw <>]\n\t\t[-cpu <>] [-mod <>] [-hdw <>]\n\t\t[-rom <>] [-ram <>] [-port1 <>] [-port2 <>] +109 Selected GUI face [%s] +110 Active GUI face has [%d] keys +111 Created key [%d], inOut [%s] +112 Found compoundString for widget [%s], value [%s] +113 Traversing widget [%s] +114 Current widget has [%d] children +201 Xt action called with wrong argc: [%d] +202 X Atom [%s] unknown +203 FSB continuation procedure not set +204 Too many messages not yet acknowledged (> [%d]).\n\tGUI logging temporarily suspended +301 Invalid option [%s] ignored +302 WM_COMMAND property bad or not set +303 No text segment found in a FSB XmString +401 X Window System fatal error +402 Face [%s] has no keys; the application resource\n\tfile probably is missing, corrupt, or unreachable diff --git a/x_func.c b/x_func.c new file mode 100644 index 0000000..ceefa6b --- /dev/null +++ b/x_func.c @@ -0,0 +1,514 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: x_func.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: x_func.c,v $ +.kind : C source +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-Nov-2000 +.keywords : * +.description : + This module implements the emulator's extended functions, that is, + functions that the real machine has not. + + References: + + Private communications with Prof. B. Parisse + +.include : config.h, machdep.h, cpu.h, x_func.h + +.notes : + $Log: x_func.c,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.15 2000/11/15 14:16:45 cibrario + GUI enhancements and assorted bug fixes: + - Implemented command-line option -batchXfer + + Revision 3.14 2000/11/13 11:11:01 cibrario + Implemented fast load/save; improved keyboard interface emulation at + high emulated CPU speed: + + - Added credits in file doc + - Implemented CPU status access functions ByteFromAddress(), + NameFromD1() + - Implemented new static function BinaryHeader(), to select an header + of binary files appropriate for the current hw configuration + - Implemented new static functions Kget()/KgetContinuation(), to load + a file from disk into the calculator, and Send/SendContinuation(), + to save a file from the calculator's memory into a disk file. + - Removed test functions TestFSB() and TestFSBContinuation() + - Implemented static helper function SetupXfer(), to setup a disk + transfer. + - Updated static function[] table + + Revision 3.13 2000/11/09 11:42:22 cibrario + *** empty log message *** + + +.- */ + +#ifndef lint +static char rcs_id[] = "$Id: x_func.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $"; +#endif + +#include +#include +#include +#include +#include +#include + +#include /* Main X header */ +#include /* Main Xt header */ + +#include "config.h" +#include "machdep.h" +#include "cpu.h" +#include "modules.h" +#include "disk_io.h" +#include "x11.h" /* ActivateFSB() */ +#include "x_func.h" +#include "args.h" +#include "debug.h" + +#define CHF_MODULE_ID X_FUNC_CHF_MODULE_ID +#include + + + +/*--------------------------------------------------------------------------- + Private functions: CPU access + ---------------------------------------------------------------------------*/ + +/* Return the A field of a DataRegister as an integer. */ +static int R2int(const Nibble *r) +{ + return( + ((int)r[0] ) | + ((int)r[1] << 4) | + ((int)r[2] << 8) | + ((int)r[3] << 12) | + ((int)r[4] << 16) + ); +} + + +/* Return the contents of the byte pointed by addr. + Memory is accessed through ReadNibble() +*/ +static int ByteFromAddress(Address addr) +{ + return (int)ReadNibble(addr) + (int)ReadNibble(addr+1) * 16; +} + + +/* Return a dynamically-allocated copy of the contents of the IDNT + object pointed by D1. D1 points to the *body* of the + RPL object, that is, to the IDNT length byte directly and *not* + to the prologue. +*/ +static char *NameFromD1(void) +{ + Address addr = cpu_status.D1; /* Points to the IDNT body */ + int len = ByteFromAddress(addr); /* IDNT length */ + char *name = XtMalloc(len+1); /* IDNT name buffer */ + int c; + + /* Read the name; toascii() is there to avoid 'strange' characters */ + for(c=0; c=0, has an architectural upper limit of 2^20, + and int are at least 2^31. + */ + cpu_status.inner_loop_max = (new_speed * INNER_LOOP_MAX) / 4; + + /* Notify the user about the speed change */ + if(cpu_status.inner_loop_max) + ChfCondition X_FUNC_I_SET_SPEED, CHF_INFO, new_speed ChfEnd; + + else + ChfCondition X_FUNC_I_MAX_SPEED, CHF_INFO ChfEnd; + + ChfSignal(); + } + +#endif +} + + +/*---------------------------------------------------------------------------*/ + + +/* This array holds the binary headers for all known hw configurations; + here, '?' is a wildcard character when reading from file + (see ReadObjectFromFile()) and is replaced by 'S' when writing + to file (see WriteObjectToFile()). +*/ +struct BinHdrMapping +{ + char *hw; + char *hdr; +}; + +static const struct BinHdrMapping bin_hdr_mapping[] = +{ + { "hp48", "HPHP48-?" }, + { "hp49", "HPHP49-?" } +}; + +#define N_BIN_HDR_MAPPING (sizeof(bin_hdr_mapping)/sizeof(bin_hdr_mapping[0])) + + +/* Return the header of binary files for current hw configuration; + return NULL if the header cannot be determined. In the latter case, + generate an appropriate condition. +*/ +static const char *BinaryHeader(void) +{ + int i; + + for(i=0; i= N_X_FUNC + || function[(int)function_code] == (XFunc)NULL) + { + ChfCondition X_FUNC_W_BAD_CODE, CHF_WARNING, function_code ChfEnd; + ChfSignal(); + } + + /* Dispatch */ + else + function[(int)function_code](function_code); +} diff --git a/x_func.h b/x_func.h new file mode 100644 index 0000000..a8e8480 --- /dev/null +++ b/x_func.h @@ -0,0 +1,109 @@ +/* ------------------------------------------------------------------------- + saturn - A poor-man's emulator of some HP calculators + Copyright (C) 1998-2000 Ivan Cibrario Bertolotti + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the documentation of this program; if not, write to + the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + For more information, please contact the author, preferably by email, + at the following address: + + Ivan Cibrario Bertolotti + IRITI - National Research Council + c/o IEN "Galileo Ferraris" + Strada delle Cacce, 91 + 10135 - Torino (ITALY) + + email: cibrario@iriti.cnr.it + ------------------------------------------------------------------------- */ + +/* +-+ */ + +/* .+ + +.identifier : $Id: x_func.h,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +.context : SATURN, Saturn CPU / HPxx emulator +.title : $RCSfile: x_func.h,v $ +.kind : C header +.author : Ivan Cibrario B. +.site : CSTV-CNR +.creation : 3-Nov-2000 +.keywords : * +.description : + Main header for the emulator's extended functions. References: + + SASM.DOC by HP (HORN disk 4) + Guide to the Saturn Processor Rev. 0.00f by Matthew Mastracci + +.include : config.h machdep.h cpu.h + +.notes : + $Log: x_func.h,v $ + Revision 4.1 2000/12/11 09:54:19 cibrario + Public release. + + Revision 3.14 2000/11/13 11:13:14 cibrario + Implemented fast load/save; improved keyboard interface emulation at + high emulated CPU speed: + + - Defined new function codes X_FUNC_KGET, X_FUNC_SEND + - Defined new status codes: X_FUNC_W_ABORTED, X_FUNC_W_FAILED, + X_FUNC_W_ABORTED, X_FUNC_W_FAILED, X_FUNC_E_NO_BIN_HDR, + X_FUNC_M_KGET, X_FUNC_M_SEND + + Revision 3.13 2000/11/09 11:42:22 cibrario + *** empty log message *** + + +.- */ + + +/*--------------------------------------------------------------------------- + Macro/Data type definitions - require cpu.h + ---------------------------------------------------------------------------*/ + +/* Extended function codes (argument of XFunction()) */ +#define X_FUNC_SET_SPEED (Nibble)0 +#define X_FUNC_KGET (Nibble)1 +#define X_FUNC_SEND (Nibble)2 + + +/*--------------------------------------------------------------------------- + Chf condition codes + ---------------------------------------------------------------------------*/ + +#define X_FUNC_I_CALLED 101 /* Function %s called */ +#define X_FUNC_I_CODE 102 /* Function code %d */ +#define X_FUNC_I_SET_SPEED 103 /* Speed set to %dMhz (%d mult.) */ +#define X_FUNC_I_MAX_SPEED 104 /* Emulator at max speed */ +#define X_FUNC_I_FILE_NAME 105 /* Transferring file name %s */ +#define X_FUNC_I_KGET 106 /* Kget start:%x end:%x hdr:%s */ +#define X_FUNC_I_SEND 107 /* Send start:%x end:%x hdr:%s */ +#define X_FUNC_W_BAD_CODE 201 /* Bad function code %d ignored */ +#define X_FUNC_W_ABORTED 202 /* Aborted by user */ +#define X_FUNC_W_FAILED 203 /* Operation failed */ +#define X_FUNC_E_NO_HALT 301 /* Cpu halt not allowed */ +#define X_FUNC_E_NO_SPEED 302 /* No speed control available */ +#define X_FUNC_E_NO_BIN_HDR 303 /* Can't determine hdr for hw %s */ +#define X_FUNC_F_xxx 401 +#define X_FUNC_M_KGET 501 /* FSB title for Kget function */ +#define X_FUNC_M_SEND 502 /* FSB title for Send function */ + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +void ExtendedFunction(Nibble function_code); diff --git a/x_func.msf b/x_func.msf new file mode 100644 index 0000000..41fe8d4 --- /dev/null +++ b/x_func.msf @@ -0,0 +1,46 @@ +$ .+ +$ .identifier : $Id: x_func.msf,v 4.1 2000/12/11 09:54:19 cibrario Rel $ +$ .context : SATURN, Saturn CPU / HPxx emulator +$ .title : $RCSfile: x_func.msf,v $ +$ .kind : Message catalog source +$ .author : Ivan Cibrario B. +$ .site : CSTV-CNR +$ .creation : 3-Nov-2000 +$ .keywords : * +$ .description : +$ Message catalog source file for the emulator's extended functions. +$ .notes : +$ . $Log: x_func.msf,v $ +$ . Revision 4.1 2000/12/11 09:54:19 cibrario +$ . Public release. +$ . +$ . Revision 3.14 2000/11/13 11:14:19 cibrario +$ . Implemented fast load/save; improved keyboard interface emulation at +$ . high emulated CPU speed: +$ . +$ . - Added new messages: 105, 106, 107, 202, 203, 303, 501, 502 +$ . +$ . Revision 3.13 2000/11/09 11:42:22 cibrario +$ . *** empty log message *** +$ . +$ .- + +$set 1 +18 X_Func + +$set 18 +101 Function [%s] called +102 Emulator's extended function #[%01X] +103 Emulator speed set to [%d]MHz +104 Emulator at maximum speed +105 Transferring [%s] +106 Kget START[%05X] END[%05X] HDR[%s] +107 Send START[%05X] END[%05X] HDR[%s] +201 Invalid function code #[%01X] ignored +202 Operation aborted by user +203 Operation failed +301 Cpu Halt requests not allowed.\n\tRebuild with CPU_SPIN_SHUTDN undefined in config.h +302 Cpu speed control not available.\n\tRebuild with REAL_CPU_SPEED defined in config.h +303 Can't determine binary header for hw [%s] +501 Load object from disk... +502 Save object to disk...