From d7531876d4cf8f97528f4a81013b1ef0cbaddfae Mon Sep 17 00:00:00 2001 From: Gwenhael Le Moine Date: Mon, 21 Mar 2022 11:05:59 +0100 Subject: [PATCH] import original from hpcalc.org --- BUGS.txt | 85 ++ Chf/Bugs | 10 + Chf/Chf.h | 371 +++++ Chf/ChfPriv.h | 155 ++ Chf/Makefile | 209 +++ Chf/Makefile_def | 68 + Chf/Makefile_sub | 40 + Chf/RCS/Bugs,v | 52 + Chf/RCS/Chf.h,v | 611 ++++++++ Chf/RCS/ChfPriv.h,v | 298 ++++ Chf/RCS/Makefile,v | 333 +++++ Chf/RCS/Makefile_def,v | 116 ++ Chf/RCS/Makefile_sub,v | 62 + Chf/RCS/chf.msf,v | 109 ++ Chf/RCS/chf.rc,v | 123 ++ Chf/RCS/chf.texi,v | 2677 +++++++++++++++++++++++++++++++++ Chf/RCS/chf_abrt.c,v | 312 ++++ Chf/RCS/chf_gen.c,v | 253 ++++ Chf/RCS/chf_hdlr.c,v | 407 +++++ Chf/RCS/chf_init.c,v | 1074 ++++++++++++++ Chf/RCS/chf_msgc.c,v | 292 ++++ Chf/RCS/chf_sig.c,v | 546 +++++++ Chf/RCS/chf_st.c,v | 295 ++++ Chf/RCS/chf_top.c,v | 192 +++ Chf/RCS/chf_win32.c,v | 212 +++ Chf/RCS/libChf.vcp,v | 509 +++++++ Chf/RCS/resource.h,v | 39 + Chf/RCS/test01.c,v | 118 ++ Chf/RCS/test01.msf,v | 43 + Chf/RCS/test02.c,v | 146 ++ Chf/RCS/test03.c,v | 112 ++ Chf/RCS/test04.c,v | 353 +++++ Chf/RCS/test05.c,v | 269 ++++ Chf/RCS/test06.c,v | 160 ++ Chf/RCS/test07.c,v | 128 ++ Chf/chf.msf | 40 + Chf/chf.rc | 99 ++ Chf/chf.texi | 2053 ++++++++++++++++++++++++++ Chf/chf_abrt.c | 199 +++ Chf/chf_gen.c | 179 +++ Chf/chf_hdlr.c | 264 ++++ Chf/chf_init.c | 780 ++++++++++ Chf/chf_msgc.c | 201 +++ Chf/chf_sig.c | 402 +++++ Chf/chf_st.c | 227 +++ Chf/chf_top.c | 115 ++ Chf/chf_win32.c | 191 +++ Chf/libChf.vcp | 485 ++++++ Chf/resource.h | 15 + Chf/test01.c | 93 ++ Chf/test01.msf | 18 + Chf/test02.c | 121 ++ Chf/test03.c | 90 ++ Chf/test04.c | 331 +++++ Chf/test05.c | 247 ++++ Chf/test06.c | 138 ++ Chf/test07.c | 103 ++ INSTALL.txt | 80 + Imakefile | 229 +++ README.txt | 49 + Saturn.ad | 2422 ++++++++++++++++++++++++++++++ args.h | 100 ++ cindex.texi | 5 + clopt.texi | 148 ++ config.h | 267 ++++ config_x.h | 65 + cpu.c | 3185 ++++++++++++++++++++++++++++++++++++++++ cpu.h | 352 +++++ cpu.msf | 64 + custom.texi | 393 +++++ debug.c | 127 ++ debug.h | 173 +++ debug.msf | 29 + dis.c | 2407 ++++++++++++++++++++++++++++++ disk_io.c | 322 ++++ disk_io.h | 100 ++ disk_io.msf | 44 + disk_io_obj.c | 317 ++++ display.c | 628 ++++++++ display.h | 93 ++ emulator.c | 701 +++++++++ flash49.c | 525 +++++++ flash49.h | 164 +++ flash49.msf | 38 + gpl.texi | 392 +++++ gpl_replace | 78 + gpl_template | 30 + hdw.c | 565 +++++++ hw_config.c | 317 ++++ introd.texi | 128 ++ keyb.c | 341 +++++ keyb.h | 88 ++ machdep.h | 75 + make_dist | 9 + make_src_dist | 44 + make_ubin_dist | 102 ++ modules.c | 1742 ++++++++++++++++++++++ modules.h | 616 ++++++++ modules.msf | 103 ++ monitor.c | 379 +++++ pack.c | 294 ++++ prep.texi | 132 ++ quick_start | 81 + romram.c | 914 ++++++++++++ romram49.c | 887 +++++++++++ run_saturn | 18 + saturn.c | 310 ++++ saturn.msf | 33 + saturn.texi | 150 ++ serial.c | 1247 ++++++++++++++++ serial.h | 144 ++ serial.msf | 62 + sutil.texi | 92 ++ sutil_48.dir | 303 ++++ sutil_49.dir | 266 ++++ t48.c | 596 ++++++++ tips.texi | 59 + using.texi | 274 ++++ util.msf | 38 + x11.c | 1974 +++++++++++++++++++++++++ x11.h | 157 ++ x11.msf | 77 + x_func.c | 514 +++++++ x_func.h | 109 ++ x_func.msf | 46 + 125 files changed, 43983 insertions(+) create mode 100644 BUGS.txt create mode 100644 Chf/Bugs create mode 100644 Chf/Chf.h create mode 100644 Chf/ChfPriv.h create mode 100644 Chf/Makefile create mode 100644 Chf/Makefile_def create mode 100644 Chf/Makefile_sub create mode 100644 Chf/RCS/Bugs,v create mode 100644 Chf/RCS/Chf.h,v create mode 100644 Chf/RCS/ChfPriv.h,v create mode 100644 Chf/RCS/Makefile,v create mode 100644 Chf/RCS/Makefile_def,v create mode 100644 Chf/RCS/Makefile_sub,v create mode 100644 Chf/RCS/chf.msf,v create mode 100755 Chf/RCS/chf.rc,v create mode 100644 Chf/RCS/chf.texi,v create mode 100644 Chf/RCS/chf_abrt.c,v create mode 100644 Chf/RCS/chf_gen.c,v create mode 100644 Chf/RCS/chf_hdlr.c,v create mode 100644 Chf/RCS/chf_init.c,v create mode 100644 Chf/RCS/chf_msgc.c,v create mode 100644 Chf/RCS/chf_sig.c,v create mode 100644 Chf/RCS/chf_st.c,v create mode 100644 Chf/RCS/chf_top.c,v create mode 100644 Chf/RCS/chf_win32.c,v create mode 100755 Chf/RCS/libChf.vcp,v create mode 100755 Chf/RCS/resource.h,v create mode 100644 Chf/RCS/test01.c,v create mode 100644 Chf/RCS/test01.msf,v create mode 100644 Chf/RCS/test02.c,v create mode 100644 Chf/RCS/test03.c,v create mode 100644 Chf/RCS/test04.c,v create mode 100644 Chf/RCS/test05.c,v create mode 100644 Chf/RCS/test06.c,v create mode 100644 Chf/RCS/test07.c,v create mode 100644 Chf/chf.msf create mode 100755 Chf/chf.rc create mode 100644 Chf/chf.texi create mode 100644 Chf/chf_abrt.c create mode 100644 Chf/chf_gen.c create mode 100644 Chf/chf_hdlr.c create mode 100644 Chf/chf_init.c create mode 100644 Chf/chf_msgc.c create mode 100644 Chf/chf_sig.c create mode 100644 Chf/chf_st.c create mode 100644 Chf/chf_top.c create mode 100644 Chf/chf_win32.c create mode 100755 Chf/libChf.vcp create mode 100755 Chf/resource.h create mode 100644 Chf/test01.c create mode 100644 Chf/test01.msf create mode 100644 Chf/test02.c create mode 100644 Chf/test03.c create mode 100644 Chf/test04.c create mode 100644 Chf/test05.c create mode 100644 Chf/test06.c create mode 100644 Chf/test07.c create mode 100644 INSTALL.txt create mode 100644 Imakefile create mode 100644 README.txt create mode 100644 Saturn.ad create mode 100644 args.h create mode 100644 cindex.texi create mode 100644 clopt.texi create mode 100644 config.h create mode 100644 config_x.h create mode 100644 cpu.c create mode 100644 cpu.h create mode 100644 cpu.msf create mode 100644 custom.texi create mode 100644 debug.c create mode 100644 debug.h create mode 100644 debug.msf create mode 100644 dis.c create mode 100644 disk_io.c create mode 100644 disk_io.h create mode 100644 disk_io.msf create mode 100644 disk_io_obj.c create mode 100644 display.c create mode 100644 display.h create mode 100644 emulator.c create mode 100644 flash49.c create mode 100644 flash49.h create mode 100644 flash49.msf create mode 100644 gpl.texi create mode 100755 gpl_replace create mode 100644 gpl_template create mode 100644 hdw.c create mode 100644 hw_config.c create mode 100644 introd.texi create mode 100644 keyb.c create mode 100644 keyb.h create mode 100644 machdep.h create mode 100755 make_dist create mode 100755 make_src_dist create mode 100755 make_ubin_dist create mode 100644 modules.c create mode 100644 modules.h create mode 100644 modules.msf create mode 100644 monitor.c create mode 100644 pack.c create mode 100644 prep.texi create mode 100755 quick_start create mode 100644 romram.c create mode 100644 romram49.c create mode 100755 run_saturn create mode 100644 saturn.c create mode 100644 saturn.msf create mode 100644 saturn.texi create mode 100644 serial.c create mode 100644 serial.h create mode 100644 serial.msf create mode 100644 sutil.texi create mode 100644 sutil_48.dir create mode 100644 sutil_49.dir create mode 100644 t48.c create mode 100644 tips.texi create mode 100644 using.texi create mode 100644 util.msf create mode 100644 x11.c create mode 100644 x11.h create mode 100644 x11.msf create mode 100644 x_func.c create mode 100644 x_func.h create mode 100644 x_func.msf 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...