Filewatcher File Search
FTP Search
  
Directory 
  
Content Search 
   
pkg://hp67-1.4-1.src.rpm:195638/hp67-1.4.tar.gz  info  downloads

hp67/ 40755    771    144           0  6503232215  10567 5ustar  neufeldusershp67/Changes100644    771    144        2232  6503230655  12164 0ustar  neufeldusersv1.4 
Mar 16, 1998: With ncurses v4, the keycode returned by the ENTER
	key on the keypad changed. Fixed, while retaining
	compatibility with the earlier ncurses library.

	Fixed a misprint in the man page.

v1.3 
Nov 25, 1997: Added hexadecimal display and input mode.

	Added options to autoload programs when hp67 starts up.

	Wrote a complex math program file, complex.txt.

	Eliminated docs.txt. The hp67.1 man page is all you need.

v1.2 
Jul 16, 1997: Fixed a bug in the integrator.txt program packaged
	with the calculator, derivatives were coming out incorrectly.

	Fixed a bug in the makefile which made the static linking against
	the C++ libraries not work correctly.

v1.1
Apr 15, 1997: Egil Kvaleberg (egilk@sn.no) converted the ASCII
	documentation to man page format.

	Fixed a bug in the hms>h function with negative numbers.

	Fixed a typo in the name of one of the statistical registers.

	Cleaned up the code, documented it some more.

	Added a "noexit" mode. If the calculator is invoked with
	any command line parameters, then the CTRL-D key no longer quits
	the program.

	Made an icon for the program. hp67.xpm

v1.0
Feb 12, 1997: First version written.
hp67/Differences100644    771    144        3525  6300156241  13030 0ustar  neufeldusersDifferences between the hp67 emulator and a real Hewlett-Packard HP-67.


The calculator has exactly four stack elements, the emulator has a
variable number, the number of values pushed on the stack. This has an
impact on the roll-up and roll-down operators.


The calculator has 26 memory elements, not all of which are accessible
with all memory functions (for instance, sto+ does not work on the
alphabetic memory elements). The emulator has an effectively unlimited
number of memory elements.


The calculator is limited to 10 digits of display with exponents less
than 100. The emulator is limited to 14 digits and exponents less than
approximately 308.


The calculator has memory elements addressable only by numbers, and by
the keys A, B, C, D, and E. The emulator has arbitrary memory
labeling.


The calculator has a return stack only two levels deep, a program can
call a subroutine which can call a subroutine, but that's the deepest
you are allowed to go. The emulator has no limit on return stack
depth.


The calculator has two clear-on-test flags, the emulator's flags can
be made clear-on-test whenever desired. The calculator has one flag
(F3) which is set on data input. This flag has no analog in the
emulator.


The calculator has 224 program steps, and scalar constants use one
program step for each keypress required to produce them. The emulator
has an effectively unlimited program space, and scalar constants use
only one step.


The calculator achieves approximately 6.5 floating point operations
per second. The emulator manages more than 10000 floating point
operations per second on a PPro 200 running Linux.


On the calculator, entering the sequence "1 ENTER 2 ENTER +" results
in a value of four, as the second ENTER duplicates the 2 in the Y
register. In the emulator, ENTER completes a number, and this sequence
results in a value of three.

hp67/FAQL100644    771    144        7317  6324701462  11350 0ustar  neufeldusersSo, here are some questions which I figure will be frequently asked
once I release this code. I'm going to try to answer them before they
become asked.


1.  Why did you implement RPN instead of the more familiar notation
used by other companies?

Well, I prefer RPN. I always goof up on the other kind of calculator,
losing my intermediate results. RPN is faster and more efficient when
you're used to it. Also, it's easier to program, as the parser doesn't
have to be very robust.


2.  Why isn't there an X interface?

There may be one later.


3.  Why did you write this in C++?

I wrote this code as a learning project for C++. Armed with two
excellent online documents, Frank Brokken and Karel Kubat's "C++
annotations", and Peter Muller's "Introduction to Object-Oriented
Programming using C++", and a C++ compiler, I endeavoured to learn the
language and write something useful. This is the result of that first
attempt. I apologize to any more experienced C++ programmers if the
source code offends them, but this is my first C++ program, and my
first object-oriented effort.


4.  Will you provide a complex data type in the future? Built-in
hyperbolic functions?

Perhaps. Probably.


5.  What about an explicit matrix type?

Less likely, but possible.


6.  What's with the source code? Why are some pure methods defined but
then the subclasses used explicitly?

This is a future expansion issue. Those pure methods represent
generalizations which I may implement at a future time.


7.  What's that "dummy()" function for in main.cc?

Wish I knew. When I take it out the code won't link, the virtual
tables for the stack and memory are not properly completed. This seems
to be because of the fact that those template classes are otherwise
only used to define global variables.


8.  Don't you know that C++ includes hash and stack classes in the
standard includes?

Yes, I know, but I found them painful to use in this context, where I
wanted the classes to have significantly more functionality. I started
out subclassing from the standard classes, and finally decided it was
cleaner to write my own from scratch. And don't get me started on
'genclass', I had all these big nasty filenames sitting around my
source directory, and I wasn't sure that the resulting code would be
portable to non-GNU C++ compilers.


9.  Can you get the mouse to work on the Linux text console?

No. Rather, that's something for ncurses developers. When ncurses
supports it, the calculator will too, without modification to the
source code.


10. Can you get the parser to accept the more traditional input? I
want to be able to type "sin (3 + 4)" and get an answer.

Maybe, some day. What this would be, though, would be a re-parser, to
accept input in that form and then pass it through to the calculator
internals in the form "3 <ENTER> 4 + sin". There would still be a
visible stack, and the calculator would still "believe" itself to be
an RPN calculator.


11. Can the calculator be used in a pipeline?

Not yet, but that would be a useful addition. If I do it, what output
strategy would seem most useful to people?
A) the X-value is output after each operation
B) the X-value is output only once, after the input runs out
C) the X-value is output when a particular keyword is delivered


12. I like the programmability, but I'm worried about name collisions
when I load more than one subroutine into memory at one time. Can you
set up a system of reduced name scoping and local variables?

If enough people express an interest, I might do this. I have a syntax
and method in mind (basically, memory labels and branch targets would
have a scoping operator, '::'). If I suspect that I am the target of a
world-wide conspiracy to get me to write a Forth compiler, then I will
become annoyed.
hp67/README100644    771    144       22146  6503231163  11572 0ustar  neufeldusersThis is the initial release of my HP-67 emulator. I got my HP-67 in
1983, secondhand. The calculator was manufactured in 1976, and still
runs perfectly. I'm used to it, I like it, and I haven't seen this
functionality in any UNIX tool I've come across, so I've now written
yet another HP-67 emulator (the first one I wrote in 1993 to run on my
Apple ][GS).

Here's what you'll find in this archive:

Changes        : a file of changes to the HP-67 program over time.

Differences    : a file of notable differences between this emulator
	and the actual HP-67. Potentially useful for anybody who wants
	to port over programs from the HP-67 program listing manual.

FAQL           : a list of anticipated questions, and my answers.

README         : that's what you're doing now.

TODO           : desirable improvements not yet done.

arguments.cc   : Parsing of the command line arguments and preload
	program name.

calcfloat.cc   : the C++ source code for the CalcFloat class 
	implementation.

calcfloat.h    : the CalcFloat class header.

config.h       : controls compiled-in features which do not govern the
	calculator's behaviour, such as which user-interface(s) to
	create.

datatypes.H    : pure method definition for calculator data types

flags.h        : the Flags class header and implementation.

hp67           : the binary executable, dynamically linked against
	libm.so.5.0.9, libc.so.5.4.44, libncurses.so.4.2,
	libg++.so.27.2.1, and libstdc++.so.27.2.1.

hp67.1         : The file docs.txt in groff format, as a man page.

hp67.h         : some configuration defines and enumerated types.

hp67.lsm       : The LSM entry for Linux archives.

hp67.ncurses3  : The v1.3 binary, dynamically linked.

hp67.ncurses3.static : The v1.3 binary, statically linked against the
	C++ libraries.

hp67.static    : the binary executable, dynamically linked against
	libm.so.5.0.9, libc.so.5.4.44, libncurses.so.4.2,
	and statically linked against libg++.so.27.2.1 and
	libstdc++.so.27.2.1. This is for those people without a C++
	compiler on their system.

hp67funcs.cc   : the action functions associated with calculator
	commands.

hp_curses.h    : some ncurses configuration and definitions.

hp_prots.h     : prototypes.

hpglobals.h    : definitions of global variables.

hyperbolics.txt: a sample program of hyperbolic functions. Load with
	r/prog and execute with "run sinh" etc.

input.cc       : implementation of the input/output methods. Currently
	only ncurses input is defined.

input.h        : definition of pure method for I/O, and header for all
	implemented I/O methods.

integrator.txt : a sample program performing Simpson's rule
	integration. See below for details.

layout.h       : the layout of the ncurses screen is defined by macros
	in this file.

main.cc        : the toplevel program.

makefile       : the makefile

memory.h       : the template defining associative arrays.

parsers.cc     : parsers for arguments to commands.

progmem.cc     : the implementation of the program steps, program
	space, and calculator functions.

progmem.h      : the header for the program steps, program space, and
	calculator functions.

stack.h        : the template defining stack space.





=== About the integrator sample program:


   The integrator.txt sample program is based on the "CALCULUS AND
ROOTS OF f(x)" program card supplied with the HP-67. It can find the
integral, derivative, or root of a supplied function. The supplied
function must be invocable via a numbered label. If the function is
already coded, and doesn't use a numbered label, then simply create a
two-line sequence in the program space such as:

label 1
goto my_function_has_an_alphabetic_label

Then, a call to function 1 just calls the other function with the
long, alphabetic label.

   To use the sample integrator package:

run hp67
enter the following:   r/prog integrator.txt

next, create a sample function, let's say 2/sqrt(pi) * e^{-x^2}
Enter this sequence:
prog
label 1
square
chs
exp
2
*
pi
sqrt
/
rtn
immed

You are now back in immediate mode, with the function called "1". Note
that if you typed "prog" and the line below the one pointed to by the
arrow did not say "<<< END OF PROGRAM SPACE >>>" then you should move
the insertion point by pressing the up and down arrow keys until that
is the case.

   Next, enter the following:

1
run set_fn_number

This tells the integrator that we will be using the function referred
to by the label "1" for all of our calculations.

To integrate that function, using Simpson's rule, with 100 intervals,
on the interval from 0 to 1, enter the following:

0
1
100
run simpson_int

The X stack element now holds the integral of the function. The
Simpson's rule integrator evaluates the function 2*(N+1) times.

   Next, find the derivative of the function at x=1:

1
run deriv_int


To demonstrate the root finder, we need a function which has
roots. So, enter the following:

goto .000

This sets the insertion mark to the line number 0. Next, use "prog" to
enter program mode, and press the up-arrow key twice. This puts you at
the end of the program space. Enter the following sequence, still in
program mode:

label 2
gosub 1
0.5
-
rtn
immed
sci

This brings you back to immediate mode with a new function, number 2,
defined as function 1 less a half. We've also set the display mode to
scientific, it's more useful for root finding because the displayed
numbers are used as an exit condition, to decide when an answer is
close enough.

   To find a root of function 2:

2
run set_fn_number

Next, you have to make a guess. Let's say that the root is somewhere
around 1.1:

1.1
run root_int


It has now computed the root of the function.  You should see the
value:
9.02  -001
That means 9.02 * 10 ^ (-1), or 9.02E-1.

However, the root is only accurate to the precision displayed on your
screen. In other words, if you wanted the root known to three digits,
you would enter "sci" mode and then set the display digits with "dsp
2". If you wanted the root to ten digits, you would have to set "dsp
9". You can do that now, and feed the result of the previous root
finding attempt in to refine your guess.

dsp 9
run root_int

It should now display:
9.021803690  -001

The analytic solution gives the following:

9.0218036899236  -001


   There is one other entry point to the package, obtained with

run deltasize_int


This takes the current X value and uses it to assign the differential
x which will be used to compute derivatives, "new_delta_int". The root
finder also takes derivatives, and it is possible for some functions
to behave badly in the root finder for the default value of
"new_delta_int". The meaning of this variable is that, when a
derivative of 'x' is being computed, its value is obtained as follows:

f'(x) =
   ( f(x * (1 + delta_x / 2)) - f(x * (1 - delta_x / 2)) ) / (x * delta_x)

The default value is 0.00005, so the derivative at x=1000 is computed
by taking (f(1000.025) - f(999.975)) / 0.05

   If you are having trouble with roots or derivatives with the
default value of new_delta_int, just enter a new value and run the
deltasize_int function.



==================================================

Tips on using preload files:

As of version 1.3, the HP-67 emulator has the ability to pre-load
program files when invoked. If given a command line argument in the
form "--program=NAME" or "-p NAME", then the program file NAME is
loaded at startup. If this switch is not given, and the environment
variable HP67PROGRAM is defined and holds the name of a file readable
by the user, then that file is loaded as the startup. Finally, if the
HP67PROGRAM environment variable doesn't exist, or does not hold the
name of a user-readable file, then the file $HOME/.hp67rc is loaded,
if it exists.

This structure is fairly flexible. If the HP67PROGRAM environment
variable holds a relative pathname, then the emulator can be made to
load different files depending on the current working directory. For
instance, if "HP67PROGRAM" is set to "localhp67prog", then whenever
the calculator is invoked from within a directory containing that
file, the file is loaded, otherwise the $HOME/.hp67rc file is
loaded. This behaviour can be overridden with the "-p NAME" switch, or
all preloading disabled with the "-i" switch.

The most common use for these preload files will probably be to define
your own shortcuts. For instance, there is no quick way to enter
negative numbers in the calculator. You have to enter the number, then
click on the "chs" button, or enter "chs" at the keyboard then press
ENTER. If your $HOME/.hp67rc file contains the lines:

label c
chs
rtn

then pressing ALT-c (or ESC-c) will change the sign of the current
number. Similar shortcuts can be used, say, for sines and cosines,
logarithms, square roots, or any other sequences which the user finds
convenient. Of course, a shortcut key can do more than just invoke a
single keypad function, there is a rich programming language available
which includes flow control, breakpoints, and step-through debugging.

==================================================


If anybody finds this emulator useful, let me know. I'm partial to
text interfaces, but if enough people say they're interested in an X
interface, I'll write one.


Christopher Neufeld
Nov 25, 1997

hp67/TODO100644    771    144         331  6436640034  11340 0ustar  neufeldusersStill to do:


X interface

Pipeline I/O

Complex or array data types

Loading of the X clipboard with the current stack top value?

More complex functions in complex.txt until complex data types become
an intrinsic.
hp67/arguments.cc100644    771    144        5676  6437273655  13241 0ustar  neufeldusers// The command line parser for options to the HP-67 program.


#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

#include "hpglobals.h"


static void usage(char *exename)
#ifdef __GNUC__
__attribute__ ((noreturn))
#endif  /* __GNUC__ */
;


static void usage(char *exename)
{
  fprintf(stderr, "\nHP67 calculator emulator.\n");
  fprintf(stderr, "By Christopher Neufeld.\n\n");
  fprintf(stderr, "Usage:\n");
  fprintf(stderr, "       %s [options]\n", exename);
  fprintf(stderr, "       --noexit,-n                  Disable CTRL-D termination of HP67.\n");
  fprintf(stderr, "       --program=program,-p program Preload this program file.\n");
  fprintf(stderr, "       --ignorercfile,-i            Don't preload rc program.\n");
  fprintf(stderr, "       --help,-h                    Print this message and exit.\n");
  fprintf(stderr, "\n");
  exit(EXIT_FAILURE);
}


void parse_options(int argc, char **argv)
{
  int alldone;
  static struct option long_options[] = {
    { "noexit", no_argument, NULL, 'n' },
    { "program", required_argument, NULL, 'p' },
    { "ignorercfile", no_argument, NULL, 'i' },
    { "help", no_argument, NULL, 'h' }
  };

  alldone = 0;
  
  while (!alldone) {
    
    switch(getopt_long(argc, argv, "np:ih", long_options, NULL)) {
    case -1:
      alldone = 1;
      break;
    case 'n':
      if (noexit)  /* Already seen this option. */
	usage(argv[0]);
      noexit = 1;
      break;
    case 'p':
      if (preload != NULL)
	usage(argv[0]);   /* Already seen this option */
      preload = optarg;
      break;
    case 'i':
      if (ignorerc)
	usage(argv[0]);   /* Already seen this option */
      ignorerc = 1;
      break;
    case 'h':
      usage(argv[0]);
    default:
      usage(argv[0]);
      break;
    }
  }
}


char *get_environment_preload(void)
{
  char *filename;
  FILE *testok;

  filename = getenv(PRELOADENVVAR);
  if (filename == NULL)
    return NULL;

  testok = fopen(filename, "r");
  if (testok == NULL)
    filename = NULL;
  else
    fclose(testok);

  return filename;
}


/* I realize there's a potential memory leak here if this function is
 * called more than once. */
char *get_rcfile_preload(void)
{
  char *homedirname;
  static char *filename;
  int bufflen;
  FILE *testok;
  int addslash;
  static int beenherebefore = 0;

  if (beenherebefore)
    fprintf(stderr, "Warning! Possible memory leak. get_rcfile_preload() called more than once.\n");

  beenherebefore = 1;
  homedirname = getenv(HOMEENVVAR);
  if (homedirname == NULL)
    return NULL;

  addslash = 0;
  bufflen = strlen(homedirname);
  if (homedirname[bufflen-1] != '/')
    addslash = 1;

  bufflen += strlen(RCFILENAME);

  filename = (char *) malloc((bufflen + addslash) * sizeof(char));
  strcpy(filename, homedirname);
  if (addslash)
    strcat(filename, "/");
  strcat(filename, RCFILENAME);

  testok = fopen(filename, "r");
  if (testok == NULL) {
    free(filename);
    filename = NULL;
  } else
    fclose(testok);

  return filename;
}
hp67/calcfloat.cc100644    771    144       20645  6436375761  13175 0ustar  neufeldusers#include <stdio.h>
#include <math.h>
#include <float.h>
#include <builtin.h>
#include <stdlib.h>
#include <limits.h>

#include "calcfloat.h"



extern int strictmode;
extern disp_mode dispmode;

CalcFloat::CalcFloat(void)
{
  value = 0.0;
  check_value();
}


CalcFloat::CalcFloat(CalcFloat const &from)
{
  value = from.value;
  check_value();
}


CalcFloat::CalcFloat(double const &from)
{
  value = from;
  check_value();
}


CalcFloat::CalcFloat(int const &from)
{
  value = from;
  check_value();
}


/* Given a number of digits after the decimal to display, the mode of
 * the display, a buffer to store the printable value, the size of
 * that buffer, and pointers to storage which will hold the number of
 * rows and columns required for the output, produce an appropriate
 * printable value for the number stored. */
void CalcFloat::printvalue(int ndigits, disp_mode dmode, char *buffer,
			   int bsize, int *rows, int *cols) const 
{
  static double uplim = 0.0;
  double aval = fabs(value);
  int decimals;
  int wholepartdigs;
  intstring anint;


// First, if the value is unprintable, produce the error message and return
  if (!value_ok()) {
    strncpy(buffer, FLTERRORSTR, bsize - 1);
    if (rows != NULL) *rows = 1;
    if (cols != NULL) *cols = strlen(buffer);
    return;
  }

// we figure out the largest number which can be represented in fixed point
  if (uplim == 0.0)
    uplim = pow(10.0, (double) MAXDIGS);

  decimals = ndigits;
  switch (dmode) {
  case HEX:
    if (aval <= ULONG_MAX) {  /* Fits in a long */
      snprintf(buffer, bsize, "%s%s%lx", value < 0 ? "-" : "",  HEXPREFIX, 
	       (unsigned long) aval);
      break;
    }
    /* Fall through to FIX */
  case FIX:            // try to print in fixed point
    if (!(aval >= uplim ||   // should not be bigger than this
	  (value != 0.0 &&   // should not be so small as to appear as zero
	   aval < 0.5 * pow(10.0, (double) -ndigits)))) {

/* No matter how many digits we are asked for, never give more than
 * the maximum display resolution, MAXDIGS */
      if (aval > 1) {
	wholepartdigs = (int) floor(log10(aval));
	if (decimals + wholepartdigs > MAXDIGS - 1)
	  decimals = MAXDIGS - 1 - wholepartdigs;
      }

      if (snprintf(buffer, bsize,"%.*f", decimals, value) == bsize) {
	fprintf(stderr, "Number too large for output buffer\n");
	abort();
      }

/* sprintf doesn't append a decimal point if the precision is
 * zero. For consistency in automatic parsing of the output, we stick
 * the decimal point back on */
      if (strchr(buffer, '.') == NULL)  // No decimal point
	append_char(buffer, '.');

      break;
    }     // else it falls through to SCI
  case SCI:
    if (snprintf(buffer, bsize, "%.*e", decimals, value) == bsize) {
      fprintf(stderr, "Number too large for output buffer\n");
      abort();
    }

    {
      char *dptr = strchr(buffer, '.');  // the decimal point
      char *eptr = strchr(buffer, 'e');  // the 'e' marking the exponent
      int expn = atoi(eptr + 1);         // the value of the exponent
      intstring anint;
      
      sprintf(anint, "%+04d", expn);     // recast it with sign and leading 0s

      if (dptr == NULL) {   // no decimal point, put one in.
	*eptr = '.';   // where the 'e' used to be
	*(eptr + 1) = 'e';  // so, construct 'e' after it
	*(eptr + 2) = 0;
      } else {
	*(eptr + 1) = 0;
      }
      strcat(buffer, anint);  // tack the exponent back on the end
    }
    break;
  case ENG:  // like SCI, but the exponent is a multiple of 3
    if (snprintf(buffer, bsize, "%.*e", decimals, value) == bsize) {
      fprintf(stderr, "Number too large for output buffer\n");
      abort();
    }
    
    char *eptr = strchr(buffer, 'e');  // Same 'e' as in the snprintf
    int expn = atoi(eptr + 1);
    *(eptr + 1) = 0;
    while (expn % 3) {
      char *dptr = strchr(buffer, '.');
      if (dptr == NULL) {  // no decimal point present, put one in
	*(eptr + 2) = 0;
	*(eptr + 1) = 'e';
	*eptr = '.';
	dptr = eptr;
	eptr++;
      }

      if (dptr + 1 == eptr)  {    // decimal is immediately before 'e'
	*(eptr + 2) = 0;    // move the 'e' over
	*(eptr + 1) = 'e';
	*eptr = '.';        // put a '0' before the decimal point
	*dptr = '0';
	eptr++;
	expn--;
      } else {   // swap the decimal and the number to the right of it
	char temp = *(dptr + 1);
	*(dptr + 1) = *dptr;
	*dptr = temp;
	expn--;
      }
    }
    sprintf(anint, "%+04d", expn);
    strcat(buffer, anint);
    
// strip multiple leading zeroes
    while (buffer[0] == '0' &&
	   buffer[1] == '0') {
      char *cptr = buffer;
      
      while (*cptr != 0) {
	*cptr = *(cptr + 1);
	cptr++;
      }
	
    }
    break;
  }

  if (rows != NULL) *rows = 1;
  if (cols != NULL) *cols = strlen(buffer);
}



/* A very simple test. A floating point number must pass through *
 * strtod leaving after itself only whitespace and comments. If in hex
 * mode, check to see if the number is a valid hex string. */
parse_codes CalcFloat::parsevalue(char const *number, char **remainder) 
{
  int negnum = 0;

  if (dispmode == HEX) {
    if (number[0] == '-') negnum = 1;

    value = strtoul(number + negnum, remainder, 0);

  } else {
    value = strtod(number, remainder);
  }

  check_value();
  if (*remainder == number)
    return PARSE_BAD;  /* Got nothing from this */
  
  if (**remainder != 0) {
      
    *remainder += strspn(*remainder, WHITESPACE);
    if (**remainder == 0 || **remainder == COMMENT)
      return PARSE_OK;
      
    return PARSE_BAD;
  }
  return PARSE_OK;
}



CalcFloat &CalcFloat::operator= (double from)
{
  this->value = from;
  check_value();
  return *this;
}


CalcFloat &CalcFloat::operator= (int from)
{
  this->value = from;
  check_value();
  return *this;
}

CalcFloat &CalcFloat::operator= (Arithdatatype const &from)
{
  if (&from == NULL) {
    this->value = 0.0;
  } else {
    this->value = ((CalcFloat &)from).value;
  }
  check_value();
  return *this;    
}



CalcFloat &CalcFloat::sin(void)
{
  this->value = ::sin(this->value);
  return *this;
}


CalcFloat &CalcFloat::cos(void)
{
  this->value = ::cos(this->value);
  return *this;
}


CalcFloat &CalcFloat::tan(void)
{
  this->value = ::tan(this->value);
  return *this;
}


CalcFloat &CalcFloat::arcsin(void)
{
  this->value = asin(this->value);
  return *this;
}


CalcFloat &CalcFloat::arccos(void)
{
  this->value = acos(this->value);
  return *this;
}


CalcFloat &CalcFloat::arctan(void)
{
  this->value = atan(this->value);
  return *this;
}


CalcFloat &CalcFloat::sqrt(void)
{
  this->value = ::sqrt(this->value);
  return *this;
}


CalcFloat &CalcFloat::square(void)
{
  this->value = sqr(this->value);
  return *this;
}


CalcFloat &CalcFloat::log_e(void)
{
  this->value = log(this->value);
  return *this;
}


CalcFloat &CalcFloat::exp_e(void)
{
  this->value = exp(this->value);
  return *this;
}


CalcFloat &CalcFloat::log_10(void)
{
  this->value = log10(this->value);
  return *this;
}


CalcFloat &CalcFloat::exp_10(void)
{
  this->value = pow(10.0, this->value);
  return *this;
}


CalcFloat &CalcFloat::recip(void)
{
  this->value = 1.0 / this->value;
  return *this;
}


CalcFloat &CalcFloat::power(Arithdatatype const &exponent)
{
  CalcFloat &ff = (CalcFloat &) exponent;
  this->value = pow(this->value, ff.value);
  return *this;
}


CalcFloat &CalcFloat::abs(void)
{
  this->value = ::abs(this->value);
  return *this;
}


double factorial(double);

CalcFloat &CalcFloat::factorial(void)
{
  this->value = ::factorial(this->value);
  return *this;
}



double factorial(double x)
{
  double wholepart;
  double fracpart;
  double fval;

  fracpart = modf(x, &wholepart);

#ifdef HAVE_LONG_DOUBLE_LIBM
  fval = expl(lgammal(x + 1));
#else
#warning Without a libm supporting long doubles your factorial
#warning results may be uncertain in the 14th or 15th decimal place
  fval = exp(lgamma(x + 1));
#endif

  if (fracpart == 0) /* If the number was an integer, make sure that
		      * the factorial is an integer, too. */
    fracpart = modf(fval + 0.5, &fval);

  return fval;
}


void CalcFloat::rec2polar(Arithdatatype *x, Arithdatatype *y)
{
  CalcFloat *px = (CalcFloat *)x;
  CalcFloat *py = (CalcFloat *)y;
  CalcFloat ang, rad;

  ang = atan2(py->value, px->value);
  rad = ::sqrt(px->value * px->value + py->value * py->value);
  *px = rad;
  *py = ang;  
}



void CalcFloat::polar2rec(Arithdatatype *r, Arithdatatype *theta)
{
  CalcFloat *pr = (CalcFloat *)r;
  CalcFloat *ptheta = (CalcFloat *)theta;
  CalcFloat x, y;

  x = pr->value * ::cos(ptheta->value);
  y = pr->value * ::sin(ptheta->value);
  *pr = x;
  *ptheta = y;
}

hp67/calcfloat.h100644    771    144       10012  6300371572  13004 0ustar  neufeldusers/* The implementation of the floating point class */

#ifndef FLOATS_H
#define FLOATS_H

#include <math.h>

#include "datatypes.H"

#define FLTERRORSTR "<<< ERROR >>>"


class CalcFloat : public Arithdatatype {

private:
  double value;


public:
/* constructors */
  CalcFloat(void);
  CalcFloat(CalcFloat const &from);
  CalcFloat(double const &from);
  CalcFloat(int const &from);

  ~CalcFloat(void);

  inline int int_value(void) const {
    return (int) value;
  }
  
  inline int value_ok(void) const {
    return !(isinf(value) || isnan(value));
  }

  void printvalue(int ndigits, enum disp_mode dmode, char *buffer, int bsize,
		  int *rows, int *cols) const;
  parse_codes parsevalue(char const *buffer, char **remainder);

  CalcFloat &operator= (double from);
  CalcFloat &operator= (int from);
  CalcFloat &operator= (Arithdatatype const &from);

// Unary + and -
//  CalcFloat &operator+ (void) const { return (CalcFloat &) *new CalcFloat(this->value); }
//  CalcFloat &operator- (void) const { return (CalcFloat &) *new CalcFloat(-this->value); }
  
// Arithmetic functions
  CalcFloat &plus (Arithdatatype const &from) {
    CalcFloat &ff = (CalcFloat &) from;
    this->value += ff.value;
    return *this;
  }

  CalcFloat &minus(Arithdatatype const &from) {
    CalcFloat &ff = (CalcFloat &) from;
    this->value -= ff.value;
    return *this;
  }

  CalcFloat &times(Arithdatatype const &from) {
    CalcFloat &ff = (CalcFloat &) from;
    this->value *= ff.value;
    return *this;
  }

  CalcFloat &div_by(Arithdatatype const &from) {
    CalcFloat &ff = (CalcFloat &) from;
    this->value /= ff.value;
    return *this;
  }
  
// Trig and trans
  CalcFloat &sin(void);
  CalcFloat &cos(void);
  CalcFloat &tan(void);
  CalcFloat &arcsin(void);
  CalcFloat &arccos(void);
  CalcFloat &arctan(void);
  CalcFloat &sqrt(void);
  CalcFloat &square(void);
  CalcFloat &log_e(void);
  CalcFloat &exp_e(void);
  CalcFloat &log_10(void);
  CalcFloat &exp_10(void);
  CalcFloat &recip(void);
  CalcFloat &power(Arithdatatype const &exponent);
  CalcFloat &abs(void);
  CalcFloat &factorial(void);
  void rec2polar(Arithdatatype *x, Arithdatatype *y);
  void polar2rec(Arithdatatype *x, Arithdatatype *y);

// Increment / decrement
  CalcFloat &operator++ (void) {
    this->value++;
    return *this;
  }

  CalcFloat &operator-- (void) {
    this->value--;
    return *this;
  }

// Relational operators
  bool operator! (void) const {
    return (this->value != 0.0);
  }

  bool operator== (Arithdatatype const &cmpr) const {
    return (this->value == ((CalcFloat const &) cmpr).value);
  }

  bool operator== (int const &cmpr) const {
    return (this->value == cmpr);
  }

  bool operator!= (Arithdatatype const &cmpr) const {
    return (this->value != ((CalcFloat const &) cmpr).value);
  }

  bool operator!= (int const &cmpr) const {
    return (this->value != cmpr);
  }

  bool operator> (Arithdatatype const &cmpr) const {
    return (this->value > ((CalcFloat const &) cmpr).value);
  }

  bool operator> (int const &cmpr) const {
    return (this->value > cmpr);
  }

  bool operator>= (Arithdatatype const &cmpr) const {
    return (this->value >= ((CalcFloat const &) cmpr).value);
  }

  bool operator< (Arithdatatype const &cmpr) const {
    return (this->value < ((CalcFloat const &) cmpr).value);
  }

  bool operator< (int const &cmpr) const {
    return (this->value < cmpr);
  }

  bool operator<= (Arithdatatype const &cmpr) const {
    return (this->value <= ((CalcFloat const &) cmpr).value);
  }


private:
/* Make sure the value conforms to HP-67 internal representation */
  void force_strict_numbers(void);
/* Round off to 10 digits. Used for strict emulation mode */
  void force_10digits(void);
/* Enforce precision limits */
  void force_2dexponent(void);
/* Check for illegal values, and possibly force HP-67 representation */
  inline void check_value(void);
  
};


inline CalcFloat::~CalcFloat(void)
{
}


inline void CalcFloat::check_value(void)
{
  if (isinf(this->value)) {
    this->value = HUGE_VAL * isinf(value);
  }
}


#endif  /* !FLOATS_H */
hp67/config.h100644    771    144         243  6271705471  12274 0ustar  neufeldusers#ifndef CONFIG_H
#define CONFIG_H

/* Configuration features. */

#define CURSES_INTERFACE
#define CURSES_MOUSE

/* #define X_INTERFACE */

#endif  /* CONFIG_H */
hp67/datatypes.H100644    771    144       10411  6300171676  13020 0ustar  neufeldusers/* Floating point class for the HP-67 calculator. Actually, the types
 * are double. */


#ifndef DATATYPES_H
#define DATATYPES_H


#include "hp67.h"

/* A pure virtual class. All calculator data must be a sub-class of
 * this. All calculator data must be capable of printing its value to
 * a buffer, or of parsing a buffer for a value. */ 
/* Printing, it must print a certain number of digits in the indicated
 * display mode. Parsing must interpret the buffer and assign the
 * internal value to match, and return 'PARSE_OK' on success,
 * 'PARSE_BAD' if the parsing encounters problems. Parsing must ignore
 * any trailing whitespace or anything after a COMMENT character, but
 * it should make "*remainder" point to comments if they are present */
class Calcdatatype {
public:
/* Indicate in *rows and *cols how much screen space will be needed to
 * print a representation of this data */
  virtual void printvalue(int ndigits, enum disp_mode dmode, char *buffer, 
			  int bsize, int *rows, int *cols) const = 0;  

  virtual parse_codes parsevalue(char const *buffer, char **remainder) = 0;
};



/* Arithdatatypes are any data types on which standard arithmetic can
 * be performed. These could include floats, ints, and complex
 * values. */ 
/* All must be able to be assigned values from a double, an int, or a
 * pointer to Hashable, and must support all arithmetic (within
 * the class), transcendental, and trigonometric functions. */
/* They must also have defined increment, decrement, nonzero, and
   relational operators, and be able to supply a value for "pi" */
class Arithdatatype : public Calcdatatype {
public:
  virtual ~Arithdatatype(void) {};

// Ability to return an integer which approximates the value
  virtual inline int int_value(void) const = 0;

// Check whether a data value is valid
  virtual inline int value_ok(void) const = 0;

// Assignment from double or int
  virtual Arithdatatype &operator= (double from) = 0;
  virtual Arithdatatype &operator= (int from) = 0;
  virtual Arithdatatype &operator= (Arithdatatype const &from) = 0;

// Unary + and -
//  virtual Arithdatatype &operator+ (void) const = 0;
//  virtual Arithdatatype &operator- (void) const = 0;
  
// Arithmetic functions. In all cases, the argument forms the right-hand
// operand in regular typography. So, the value of the argument is
// added to, subtracted from, post-multiplied by, or divided into the
// value in the current cell.
  virtual Arithdatatype &plus(Arithdatatype const &a) = 0;
  virtual Arithdatatype &minus(Arithdatatype const &a) = 0;
  virtual Arithdatatype &times(Arithdatatype const &a) = 0;
  virtual Arithdatatype &div_by(Arithdatatype const &a) = 0;
  
// Trig and trans
  virtual Arithdatatype &sin(void) = 0;
  virtual Arithdatatype &cos(void) = 0;
  virtual Arithdatatype &tan(void) = 0;
  virtual Arithdatatype &arcsin(void) = 0;
  virtual Arithdatatype &arccos(void) = 0;
  virtual Arithdatatype &arctan(void) = 0;
  virtual Arithdatatype &sqrt(void) = 0;
  virtual Arithdatatype &square(void) = 0;
  virtual Arithdatatype &log_e(void) = 0;
  virtual Arithdatatype &exp_e(void) = 0;
  virtual Arithdatatype &log_10(void) = 0;
  virtual Arithdatatype &exp_10(void) = 0;
  virtual Arithdatatype &recip(void) = 0;
  virtual Arithdatatype &power(Arithdatatype const &exponent) = 0;
  virtual Arithdatatype &abs(void) = 0;
  virtual Arithdatatype &factorial(void) = 0;
  virtual void rec2polar(Arithdatatype *, Arithdatatype *) { }
  virtual void polar2rec(Arithdatatype *, Arithdatatype *) { }

// Increment / decrement
  virtual Arithdatatype &operator++ (void) = 0;
  virtual Arithdatatype &operator-- (void) = 0;

// Relational operators
  virtual bool operator! (void) const = 0;
  virtual bool operator== (Arithdatatype const &cmpr) const = 0;
  virtual bool operator!= (Arithdatatype const &cmpr) const = 0;
  virtual bool operator> (Arithdatatype const &cmpr) const = 0;
  virtual bool operator>= (Arithdatatype const &cmpr) const = 0;
  virtual bool operator< (Arithdatatype const &cmpr) const = 0;
  virtual bool operator<= (Arithdatatype const &cmpr) const = 0;
  virtual bool operator== (int const &cmpr) const = 0;
  virtual bool operator!= (int const &cmpr) const = 0;
  virtual bool operator> (int const &cmpr) const = 0;
  virtual bool operator< (int const &cmpr) const = 0;

};




#endif  /* DATATYPES_H */
hp67/flags.h100644    771    144        2676  6300354662  12152 0ustar  neufeldusers#ifndef FLAGS_H
#define FLAGS_H

#include <bool.h>

#include "memory.h"


/* Flags. Notice that flags matching the strings "2" or "3" are cleared
 * on reading if "MAGIC_FLAGS" is defined */


class fstruct
{
public:
  unsigned setting:1;
  unsigned clr_on_test:1;

  inline fstruct(void) { setting = clr_on_test = FALSE; }
  inline fstruct(bool arg) { setting = arg; clr_on_test = FALSE; }
};


class Flags {

private:
  HashTable<fstruct> *flagelems;

public:
  inline Flags(void) { flagelems = new HashTable<fstruct>; };
  inline ~Flags(void) { delete flagelems; };

  inline void set_flag(char const *label, int test_clears);
  inline void clr_flag(char const *label);
  inline bool test_flag(char const *label);
  inline void clr_all_flags(void) { delete flagelems;
				    flagelems = new HashTable<fstruct> ; }
};

inline void Flags::set_flag(char const *label, int test_clears = FALSE)
{
  (*flagelems)[label].setting = TRUE;
  (*flagelems)[label].clr_on_test = (test_clears ? TRUE : FALSE);
}

inline void Flags::clr_flag(char const *label)
{
  (*flagelems)[label].setting = (*flagelems)[label].clr_on_test = FALSE;
}

inline bool Flags::test_flag(char const *label)
{
  bool retval = (*flagelems)[label].setting;

#ifdef MAGIC_FLAGS
  if (label[1] == 0 && 
      (label[0] == '2' ||
       label[0] == '3'))
    clr_flag(label);
#endif  /* MAGIC_FLAGS */

  if ((*flagelems)[label].clr_on_test)
    clr_flag(label);

  return retval;
}

#endif  /* !FLAGS_H */
hp67/hp67.1100644    771    144      106102  6474337276  11616 0ustar  neufeldusers.\" TAB-
.TH HP-67 1 "" "V1.0"
.SH NAME
.I hp67
\- a scientific calculator in Reverse Polish Notation

.SH SYNOPSIS
.B hp67 
[\-n|\-\-noexit] [\-\-program=program|\-p program]
[\-\-ignorercfile|\-i] [\-\-help|\-h]

.SH DESCRIPTION
.PP
This program emulates an HP-67 calculator with a few minor variations.
This calculator uses the 'Reverse Polish Notation' favoured by
Hewlett-Packard.
.PP
Reverse Polish Notation is somewhat different from the "forward"
notation available on many, probably most, handheld calculators. It is
the notation used by the Forth system of languages. Here are some
examples of how the notation works:
.PP
To evaluate:       (3 + 4*7) / (2 + 8^3)
.PP
You hit the following sequence of keys:
.RS
.nf
3  <ENTER>
4  <ENTER>
7  *
   +
2  <ENTER>
8  <ENTER>
3  y^x
   +
   /
.fi
.RE
.PP
The <ENTER> key is used to separate different numbers entered
consecutively.  As numbers are entered they are pushed onto a LIFO
stack.  The most recently entered number, at the top of the stack, is
called the 'X' value, the next most recently entered number is the 'Y'
value.  On the screen the 'X' appears nearest the bottom, with 'Y'
immediately above it.  Unary operations such as 'sin' pop a single
number (X) off the stack, act on that number, and push the result onto
the stack.  Binary operations such as '/' pop two numbers off the stack,
and divide the first number popped (X) into the second (Y), then push
the result onto the stack.  This has the effect of reducing by one the
total number of elements on the stack.  Look over the above example
until it's clear.  Another register, 'LSTX', is not displayed on the
stack.  This holds the value which 'X' held before the last operation
was made.  Not all operations will update this number.  LSTX is most
commonly used in error correction, such as if you typed '*' instead
of '/', and in the implementation of automatic constants. 

.SS OPTIONS
.TP
.I "\-n, \-\-noexit"
Disable the CTRL-D exit key.
This
behaviour is desirable if the program is run from within a
dedicated xterm,
such as
.I via
the following shell script:
.IP
#! /bin/sh
#

/usr/X11R6/bin/color_xterm $@ -ls -tn xterm-color \\
-e hp67 --noexit &
.IP
This script can then be invoked, for instance, as follows:
.IP
hp67.sh -iconic -geometry +700+400
.IP
to produce a dedicated xterm window which cannot be aborted, short of
sending the program a killing signal.
.TP
.I "\-p program, \-\-program=program"
Load the program space with the named program during. Equivalent to
issuing the command
.B r/prog program
from the keyboard immediately when the program begins.
.TP
.I "\-i, \-\-ignorercfile"
Don't preload any programs.
.TP
.I "\-h, \-\-help"
Issue a usage message and exit.

.SH "SPECIFICS OF THE HP-67 EMULATOR"
.PP
.SH "Command entry format"    
.PP
Numbers must be entered alone on the line.  They may be expressed in
fixed or scientific notation.  Examples of valid entries on the command
line are:
.RS
12.493      12.4e-5    .008E4   1.2E+4
.RE
.PP
Note that it is not normally possible to enter negative numbers
directly at the command line prompt (but see the entry for '{' in the
section on "magic" keys in curses mode.)
.PP
Commands and operations may be entered alone on the command line, or
immediately after a valid number, with argument if allowed.  The argument
must be separated from the command by one or more blanks or tab
characters.  Examples of valid commands and operations are:
.RS
.nf
+
sto speedoflight
dsp 9
fix
.fi
.RE
.PP
See the list of operations for details of which commands require
operands. 
.PP
If an operation pops a non-existent stack element, it is not an error,
a zero is obtained. Similarly, an attempt to read an uninitialized
memory element returns a zero.

.SH "Display layout"
.PP
The calculator requires at least an 80x22 screen to work, and the
layout of buttons is most logical when the number of columns is
exactly 80. The upper portion of the screen consists of buttons
showing the currently valid commands to the calculator. The right side
has a numeric keypad, while the lower-left portion is responsible for
interaction with the user. The lower-middle portion shows the most
recently accessed memory elements, up to 15 if the number of screen
rows is large enough, and the first 8 characters of the element's
label (assuming an 80 column screen).
.PP
In immediate (interactive) mode, a number of stack elements are
displayed in this region, with X at the bottom, Y above it, and higher
elements above those.
.PP
In program mode, the current insertion and run point is shown with
an arrow to its left. Above and below it are neighbouring program
elements. New elements are inserted after the position of the arrow,
and when the calculator runs, the arrow always shows the next step
which will be executed.
.PP
A stepping mode also exists. If a program is interrupted with a
keypress, or if "step" is entered in immediate mode, then the display
shows part of the stack, and part of the program. The program window
shows the next step which will be performed, the earlier step in the
program memory (not necessarily the last step performed, if that was a
branch), and the step after the next one to be performed. Every time
"step" is entered, one program step is executed and the insertion/run
pointer is updated.

.SH "Curses magic behaviour"
.PP
When operating in curses mode, hp67 performs some extra actions for
certain input sequences.
.PP
The '#' is a comment character for almost all functions. The disk
I/O functions ignore it, and so can load file names containing the
character, but all other operations consider that the argument ends
with the last non-whitespace before the '#' character appears. A
comment may not be the only content of the input line.
.PP
If a line of input begins with a character which identifies it as a
number, such as a digit or decimal point, then it continues to accept
characters until such time as one shows up which is not part of a
valid float. The valid float is passed to the interpreter, while the
remaining text waits on the input line. So, if the sequence "102s" is
typed, it is as if the number "102", then <ENTER>, then "s" were
typed. If the sequence "45e+l" is entered, it is interpreted as "45",
<ENTER>, "e+l". Note that there is at present no command which begins
"e+l", so this is likely not to appear in normal use. If the sequence
"1e4e" is typed, it is interpreted as "1e4" (10000), <ENTER>,
"e". This number watching allows one to avoid an extra keypress, it is
legal to type "30 ENTER 40 +", the result is seventy.
.PP
Note that the following sequence on the command line:     "3+4" <ENTER>
may not do what you expect. This has the effect of pushing 3 onto the
stack, adding it to whatever was on the stack before, then pusing 4
onto the stack.
.PP
Now, the input line supports a few special command sequences.
.PP
In the following, the notation "M-a" means "meta (lowercase) a".
This sequence can be produced by holding down the ALT key on terminals
which support this, or prefixing the 'a' keypress with ESC.
.PP
The following are 'hot keys'. If they are entered in the first position
on a blank input line their functions will be invoked:

.IP "<DELETE>"
In immediate mode, invokes the "clx" function to delete
the top element of the stack. In program mode, deletes the
current program line.
.IP "M-<SPACE>"
In immediate mode, invokes the "step" function.
.IP "M-<key>"
If <key> is a printable key, in immediate mode this
invokes the command "run <key>", running a subroutine
identified by the single letter label <key>. In program
mode, this invokes the command "label <key>", creating an
entry point identified by the single letter label <key>.
.IP "<CTRL>-D"
In immediate mode, exits the calculator. In program mode,
returns to immediate mode.
.IP "UP-ARROW"
In program mode, moves the program insertion/execution
pointer back one space.
.IP "DOWN-ARROW"
In program mode, moves the program insertion/execution
pointer forward one space (without executing the step).
.IP "{"
As the first element on the line, the '{' character
disables (for the current input line) the magic
floating-point number parsing described above. This is
intended for future expansion, when input types such as
complex numbers may use different characters to mark
input. It can be used to enter negative numbers at the
command line.
.IP "CTRL-L"
forces curses to redraw the entire window from scratch.
Useful if the window is resized, or if some other
output to the screen intrudes.
.PP
All control keys not listed above are filtered at the keyboard read
stage and can never be embedded into labels or commands. The TAB key is
immediately translated into a blank at read time.
.PP
On terminals which support ncurses mouse events, the function list
at the top of the screen, and the keypad on the right side, are both
clickable to invoke the corresponding action. Commands which take no
arguments are sent with an implied <ENTER> following them, so simply
pressing the text of "sin" calculates the sine of the current X
value. Commands which take arguments are sent with an implied BLANK
following them, allowing the user to enter the argument. Numbers on
the keypad, of course, are sent without modification.

.SH "LIST OF COMMANDS"
.PP
The following commands are available either in programming or in user
mode:
.de CC
.IP "\\\\fB\\$1\\\\fP \\\\h'|7'\\$2 \\\\h'|35'\\\\fI\\$3\\\\fP \\\\h'|54'\\$4"
..
.CC "+"   "Addition operator"              "X \(<- Y + X"    "LSTX"
This operator takes no arguments.   It pops the top two elements of
the stack and adds them together, pushing the result on the stack. 
.CC "-"   "Subtraction operator"           "X \(<- Y - X"    "LSTX"
As above, but it subtracts.
.CC "*"   "Multiplication operator"        "X \(<- Y * X"    "LSTX"
As above, but it multiplies. 
.CC "/"   "Division operator"              "X \(<- Y / X"    "LSTX"
.CC "!"   "Factorial operator"             "X \(<- !X"       "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with the factorial of the number.  The factorial is the product
of all natural numbers less than or equal to the number.  If the value
in X is not an integer, it returns the real-number generalization of
the factorial, the gamma function of 'X+1'.  An error occurs if X is a
negative integer, or if an overflow occurs.
.CC "recip" "Reciprocal operator"          "X \(<- 1 / X"    "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with its reciprocal. 
.CC "chs" "Change sign operator"           "X \(<- -X"       "LSTX"
This operator takes no arguments. It replaces the value in the X
register with its negative value.
.CC "sin" "Sine operator"                  "X \(<- sin(X)"   "LSTX"
This operator takes no arguments.   It repalces the value in the X
register with its sine, where X is interpreted as an angle in the units
of the 'THETA' flag.  See below for information on changing this flag. 
.CC "cos" "Cosine operator"                "X \(<- cos(X)"   "LSTX"
As above, but for cosine.
.CC "tan" "Tangent operator"               "X \(<- tan(X)"   "LSTX"
As above, but for tangent.
.CC "asin" "Inverse sine operator"         "X \(<- asin(X)"  "LSTX"
As above, but for inverse sine.
.CC "acos" "Inverse cosine operator"        "X \(<- acos(X)"  "LSTX"
As above, but for inverse cosine.
.CC "atan" "Inverse tangent operator"       "X \(<- atan(X)"  "LSTX"
As above, but for inverse tangent.
.CC "sqrt" "Square root operator"           "X \(<- sqrt(X)" "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with its square root.  This won't work for negative numbers. 
.CC "square" "Square operator"             "X \(<- X ^ 2"    "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with its square. 
.CC "ln"  "Natural logarithm operator"     "X \(<- ln(X)"    "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with its natural logarithm. 
.CC "exp" "Exponential operator"           "X \(<- exp(X)"   "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with e^X. 
.CC "log10" "Base 10 logarithm"             "X \(<- log(X)"   "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with its base 10 logarithm.
.CC "exp10" "Power on 10 operator"         "X \(<- 10^X"     "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with 10^X.
.CC "y^x" "Power operator"                 "X \(<- Y ^ X"    "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with the value Y^X.  This returns errors if X is negative and Y
is not an integer, or if X is zero and Y is less than or equal to zero.
.CC "abs" "Absolute value operator"        "X \(<- abs(X)"   "LSTX"
This operator takes no arguments.   It replaces the value in the X
register with its absolute value.
.CC "dsp" "Set display precision"
This operator takes one argument, an integer between 0 and 14
inclusive. In 'fix' and 'sci' modes this sets the number of digits to
appear to the right of the decimal point.  In 'eng' mode this sets the
total number of digits to appear on the screen to 'n+1'.  In other
words, changing from 'sci' to 'eng' mode does not change the precision
of the display.  This operator is available with indirection.  If the
argument is "(i)" it will set the number of digits to the greatest
integer value of the I register, if that value lies in the correct
range. 
.CC "fix" "Set fixed display mode"
This operator takes no arguments.   It sets the display of stack
elements to fixed point notation.  Numbers which cannot be displayed
in this notation in the precision specified by 'dsp' will be displayed
instead in scientific notation.
.CC "sci" "Set scientific notation display mode"
This operator takes no arguments.   It sets the display of stack
elements to scientific notation.  In this form all numbers are written
with a single digit preceding the decimal point, and a four digit
exponent on the right of the display.
.CC "eng" "Set engineering notation display mode"
As above, but between one and three digits precede the decimal point,
and the exponent is always a multiple of three.
.CC "hex" "Set hexadecimal display mode"
In hexadecimal mode, all numbers are displayed in hexadecimal
format, as the next integer closer to zero (i.e. rounding down for
positive values, and up for negative values). In hexadecimal mode the
magic input completion is disabled, and numbers can be entered as
either octal, decimal, or hexadecimal integers. A number beginning
with '0x' is interpreted as hexadecimal, otherwise a number beginning
with '0' is octal. In all other cases the number is treated as a
decimal value.
.CC "deg" "Set degrees mode"               "THETA  =  degrees"
This operator takes no arguments.   It sets the internal 'THETA' flag
to degrees.  All subsequent angles are interpreted in units of degrees,
and functions which return angles return them in degrees. 
.CC "rad" "Set radians mode"               "THETA  =  radians"
As above, but it sets angles to radians.
.CC "grd" "Set gradians mode"              "THETA  =  gradians"
As above, but it sets angles to gradians.   There are 400 gradians in
a circle, and if you use this mode even once I'll be surprised. 
.CC "sto" "Store to memory register"       "<label> \(<- X"
This operator takes a single argument.   That argument can be any
string which does not contain a '#' and does not begin with a '.'.
The current X value will be written into the memory identified by this
label.  If no such memory register exists, it is created.  This
operator can also use indirect addressing.  If the label is "(i)", the
current value of the I register is extracted, converted to an integer,
then to a character string, and passed as if it were the argument
typed on the command line.  So, if I holds the value 214.1, the X
register is stored to the memory element labelled "214".
.CC "rcl" "Recall from memory reg."        "X \(<- <label>"
As above, but it recalls from memory and pushes the number obtained
onto the stack.  The current X value is not lost, it is merely pushed up
with the rest of the stack. 
.CC "sto+" "Add to memory register"        "<label> \(<- <label>+X"
This operator takes one argument, the memory label.   It acts like the
'sto' operator, but rather than replacing the value in that memory
register, it adds the current X value to it.  This operation is
available with indirection.
.CC "sto-" "Subtract from memory reg."      "<label> \(<- <label>-X"
As above, but it subtracts X from the memory register.
.CC "sto*" "Multiply into memory reg."      "<label> \(<- <label>*X"
As above, but it multiplies X into the memory register.
.CC "sto/" "Divide into memory reg."        "<label> \(<- <label>/X"
As above, but it divides X into the memory register.
.CC "x<>y" "Swap X and Y"                   "X \(<-\(-> Y"
This operator takes no arguments.   It exchanges the X and Y elements
on the stack.  This is useful, for instance, if you want to evaluate
X^Y.  You can first swap X and Y, then use the 'pow' operator. 
.CC "r>p" "Rectangular to polar conversion"  ""              "LSTX"
This operator takes no arguments. It converts the pair (X,Y) into
polar form.
.RS
.nf
               X \(<- sqrt(X^2 + Y^2)
               Y \(<- atan2(Y,X)
.fi
.RE
.CC "p>r" "Polar to rectangular conversion" ""               "LSTX"
This operator takes no arguments.   It reads the X register as a
distance and the Y register as an angle, and converts to cartesian form.
This has the effect:    
.RS
.nf
                X \(<- X * cos(Y)
                Y \(<- X * sin(Y)   
.fi
.RE
.IP
The former Y value is lost.
.CC "d>r" "Degrees to radians conv."       "X \(<- X * 180/pi" "LSTX"
This operator takes no arguments.   It converts the value in the X
register from degrees to radians.  Note that it does not change the
value of the 'angmode' internal flag. 
.CC "r>d" "Radians to degrees"             "X \(<- X * 180/pi" "LSTX"
This operator takes no arguments.   It converts the value in the X
register from an angle in radians to one in degrees.  Note that it does
not change the value of the internal 'THETA' flag.
.CC "pi"  "Numerical value of pi"          "X \(<- pi"
This operator takes no arguments.   It is shorthand for entering the
first 19 decimal places of 'pi'.  It pushes 'pi' onto the stack.  The X
register is not lost, it moves into the Y register, and so on down the
stack. 
.CC "h>hms" "Hours to hours/minutes/seconds conversion" ""     "LSTX"
This operator takes no arguments.   It reads the X register as a
number of hours, and converts it to hh.mm.ssss form. See the 'hms+'
and 'hms>h' operators description for more information.
.CC "hms>h" "Convert hours/minutes/seconds to hour" ""       "LSTX"
This operator takes no arguments. It reads the X register as a
number in hh.mm.ssss form and converts the result to a fraction of an
hour. So, 1.30 would become 1.5, since one hour and thirty minutes is
equal to an hour and a half.
.CC "hms+" "Add in hours/min/sec fmt."      "X \(<- X + Y hms" "LSTX"
This operator takes no arguments.   It adds X and Y as if they were
in the form:   hh.mmssss. That is, the integer part of the number is
taken as hours, the first two digits after the decimal point are taken
as minutes, and all digits after that are interpreted as seconds. For
instance, 3.182014 would become 3 hours, 18 minutes, 20.14 seconds.
After the addition, the result is adjusted so that the seconds and
minutes fields do not equal or exceed sixty. For example:
.RS
.nf
        1.4020   <ENTER>
        1.3052
        hms+
.fi
.RE
.IP
yields:  3.1112
.CC "int" "Integer roundoff"               "X \(<- (int)X"   "LSTX"
This operator takes no arguments.   It rounds the number in the X
register to the next integer closer to zero.
.CC "frac" "Fractional part"             "X \(<- frac(X)"     "LSTX"
This operator takes no arguments.   It discards the integer portion of
X.  If X is negative, it still discards the whole part, so that
.RS
        frac(-1.2) = -0.2
.RE
.CC "round" "Round off to displayed value"
This operator takes no arguments.   It rounds off the value in the X
register to the actual value displayed on the screen.  If the X register
holds 1.2284, and the display mode is 'fixed' and 'dsp 2', then the
screen will display the value '+1.23'. The 'rnd' function rounds off the
internal representation to match.
.CC "rci" "Recall from I register"
This operator takes no arguments.   It pushes the current I value onto
the stack, pushing the rest of the stack down to accomodate it.
.CC "sti" "Store in I register"         "I \(<- X"
This operator takes no arguments.   It stores the current X value in
I. The former value of I is lost, nothing else is changed. Note that
this function cannot be replaced by "sto (i)" as that would invoke the
indirection behaviour of the "sto" function.
.CC "dsz" "Decrement; skip if zero"     "I \(<- I - 1"
This operator takes no arguments.   It decrements the I register.  If
a program is executing, and the I register is zero after the decrement,
then the next program step is skipped. 
.CC "dsz(i)" "Decrement indirect; skip if zero"
This operator takes no arguments.   It decrements the memory register
whose label matches I. If the register is zero after the decrement and a
program is executing, the next program step is skipped.
.CC "isz" "Increment; skip if zero"     "I \(<- I + 1"
As 'dsz', but it increments the register.
.CC "isz(i)" "Increment indirect, skip if zero"
As 'dsz(i)', but it decrements the register.
.CC "x<>i" "Exchange X and I"            "X \(<-\(-> I"
This operator takes no arguments.   It exchanges the current X and I
values.
.CC "stat+" "Add statistical data pair" ""                    "LSTX"
This operator takes no arguments.   It takes the current X and Y
values and updates internal registers containing the sum of: X, X^2,
Y, Y^2, and X*Y.  The X value is replaced by the total number of data
pairs collected.  This function can also be used if you are only
processing X values, rather than X,Y pairs.  Just ignore the results
from the 'Y' values. This operator updates memory elements accessible
to any running program. These are, "_stats_sumx", "_stats_sumx2",
"_stats_sumy", "_stats_sumy2", "_stats_sumxy", "_stats_n", and
contain the sum of values of X, X squared, Y, Y squared, X * Y, and
the number of points, respectively.
.CC "stat-" "Subtract statistical data pair" ""               "LSTX"
As above, but it subtracts out the values from the internal
registers.  This is usually used to remove erroneous data pairs entered
with 'sum+'. 
.CC "avg" "Obtain average X and Y values" ""                 "LSTX"
This operator takes no arguments.   It replaces the current X and Y
values with the average values of X and of Y entered with 'sum+'. 
.CC "sdev" "Obtain standard deviation of X and Y values" ""   "LSTX"
This operator takes no arguments.   It replaces the current X and Y
values with the standard deviations of X and of Y entered with 'sum+'. 
.CC "%"   "Percentage operator"            "X \(<- Y * X/100" "LSTX"
This operator takes no arguments.   It multiplies the top two elements
on the stack, and then divides by 100. 
.CC "%chg" "Percent change operator"     "X \(<- (X-Y)*100/Y" "LSTX"
This operator takes no arguments.   It changes the value in the X
register to the percentage of Y by which X differs from Y.
.CC "clx" "Clear X value"                  "X \(<- Y"
This operator takes no arguments. It pops the stack, discarding the X
value and promoting the former Y value to the new X value. Note that
this is subtly different from the clear operator on the HP-67, repeated
invocations of which do not clear the entire stack.
.CC "rdown" "Roll stack down"
This operator takes no arguments.   It rolls the stack so that the X
value goes to the bottom of the stack, and all other registers move up
one position, making the former Y value into the new X value.  This
differs from the HP-67 roll down operator in that it rolls only active
stack elements.  The HP-67 has a stack size of four, and the X register
is always moved into the fourth position, even if fewer than four stack
elements were in use. 
.CC "rup"    "Roll stack up"
This operator takes no arguments.   It rolls the stack so that the
bottom stack element moves into the X position, and all other stack
elements move one level deeper.  This differs from the HP-67 operator in
the same way as 'rdown' above. 
.CC "lastx" "Retrieve LastX register"
This operator takes no arguments.   It pushes the current contents of
LSTX onto the stack. 
.CC "clstk" "Clear stack space"
This operator takes no arguments.   It deletes all stack elements.
.CC "clreg" "Clear memory registers"
This operator takes no arguments.   It deletes all memory registers,
freeing the memory and returning it to the machine. 
.CC "goto" "Move the program counter to a position"
This operator takes one argument. It moves the program counter to
the label or line number represented by the argument. Line numbers are
in the form of a decimal point followed immediately by a numeric
string. Valid labels cannot begin with a decimal point, so there is no
ambiguity. It can be invoked with indirection, in which case it
searches for the label which matches the integer value of the I
register, for positive I, or steps back exactly N steps for negative
I, where N is the value of int(I).
.CC "R/S"    "Run/stop"
This operator takes no arguments. It halts program execution
immediately and returns to user mode. The current return stack is not
lost, so the program can be stepped through from this point without
losing subroutine information. The program will continue after this
instruction if 'run' is entered without arguments. Note that a running
program hitting a R/S statement is, technically, an error, so the
calculator will sound or flash an alert if the terminal supports that.
.CC "sf"  "Set flag"
This operator takes one or two arguments, the first one a label or
the indirection operator, and sets a binary flag with that label
identifier. If the argument list ends in the string " clr", then this
is stripped from the label and the resulting flag is a clear-on-test
flag. The clear-on-test status is updated every time that the 'sf'
operator is called, so a given flag can be clear-on-test in one part
of the program, and explicit-clear-only in another. The program keeps
a list of all set flags. This is used to pass information (usually on
flow) between different parts of a program. If the flag is already set
it has no effect.
.CC "cf"  "Clear flag"
Unsets the flag named by the argument. If the flag was not already
set it is not an error.
.PP
The following commands are available only in immediate mode:
.CC "run" "Run a program"
This operator can take one argument, or no arguments. If invoked
with no arguments it runs from the current program counter
location. If invoked with an argument it runs from that label.  If the
target label is a single character it can be invoked with the
M-<key> hot key.
.IP
While a program is running it can be stopped by pressing any key.
.CC "prog"  "Enter programming mode"
This operator takes no arguments. It allows the user to key in
programs. hp67 re-enters user mode when 'immed' is entered.
.CC "step" "Step through a program"
Executes the command under the current insertion pointer, and
advances the pointer to the next element.
.CC "r/prog" "Load a program from a disk file"
This operator takes one argument, the pathname of the text file
which contains the program to load. The program loaded is inserted
after the current program counter location. Care should be taken that
this doesn't insert it into the middle of another program segment, or
that one will be trashed efficiently. This function cannot be used
inside a program, and one loaded file cannot call another file to
load.
.PP
Variables and memories can be loaded this way also. When loading
the program starts out in programming mode, but if the token 'immed'
appears in the file then subsequent lines are interpreted as if they
had been issued in immediate mode. They can put numbers on the stack,
act on them with operators, store them to memory labels, exactly as if
the commands had been typed at the keyboard. A later 'prog' token can
switch back to programming mode. The file is parsed until an error or
the end of file is encountered. Be careful not to leave a blank line
at the end of the file, since a blank line is interpreted as the
<<\~ENTER\~>> command.
.CC "w/prog" "Save program elements"
This operator takes one argument, the pathname of the text file
which should be written with the program instructions. The text is
written out, including comments.
.CC "w/data" "Save memory elements"
This operator takes one argument, the pathname of the text file
which will be used to store non-zero memory elements. The format is
compatible with the input required 'r/prog' operator, so loading the
file with that command restores the memory as it appeared when the
'w/data' command was run. It does not erase memory elements which are
present when the file is re-loaded, unless those elements have the
same label as the ones being loaded. Note that there is a possibility
for unexpected behaviour here, if a program element existed and was
exactly zero, then was saved, and re-loaded after the element was
assigned a non-zero value, then the new value is not reset to zero. To
ensure that the program memory is exactly the same as that which was
saved, it is recommended that you invoke 'clreg' before loading the
memory.
.PP
The following commands are available only in programming mode:
.CC "label"  "Create a label"
This operator takes one argument, a printable ASCII string which
does not begin with a period. It is used as the target of branches and
'run' commands. For single-character labels you can use the shorthand
M-<key>.
.CC "gosub"  "Go to a subroutine"
This operator takes one argument, a valid label or the indirection
notation. It pushes the return address onto an internal stack and
continues execution from the label. The 'rtn' statement returns from the
subroutine. As with other features of this program, subroutine nesting
is limited only by the total memory available.
.CC "rtn" "Return from a subroutine"
This operator takes no arguments. It pops a return address and
continues execution from there. If there are no more entries on the
stack it returns to the user mode.
.CC "f?"  "Check flag"
This operator takes one argument. If the flag pointed to by the label
is not set then the next program step is skipped. If the flag is a
clear-on-test flag, then it is cleared.
.CC "x==0" "Check X=0?"
This operator takes no arguments. If the value contained in the X
register is not zero then the next program step is skipped.
.CC "x==y" "Check X=Y?"
As above, but the condition for executing the next program step is
that X must equal Y.
.CC "x!=0" "Check X not equal to zero?"
See above, you figure it out.
.CC "x!=y" "Check X not equal to Y?"
See above, you figure it out.
.CC "x<0" "Check X is negative?"
See above.
.CC "x<=y" "Check X is not greater than Y?"
See above.
.CC "x>0"  "Check X is positive and non-zero?"
See above.
.CC "x>y" "Check X is greater than Y?"
See above.
.CC "clprg" "Clear program space"
This operator takes no arguments. It can only be executed from
programming mode. It erases all program elements.
.CC "immed" "Exit programming mode"
This operator takes no arguments. It exits programming mode and
enters user mode. <CTRL>-D is a shorthand hot key for this.

.SH "MORE NOTES ON PROGRAMMING HP67 CALCULATOR"
.PP
This program maintains the concept of a program counter. The program
counter marks the place where the next program step will be inserted if
you're writing a new program, and marks the place where the next
instruction will be executed in user or stepping mode. It is incremented
immediately before the current command is executed. If the program hits
an 'rtn' statement, then, the program counter is pointing to the
statement after the calling 'gosub' when it returns. Typing 'run'
without arguments at this point will send the program on from there.
.PP
When the user or program issues a 'goto label' command the label is
searched forward from the statement after the current program counter,
if necessary cycling round at the end and coming back from the first
program location, until it either finds the label or returns to its
starting point. The latter results in an error. Notice that labels 
.B need not be unique, 
and the 'goto', 'gosub', and 'run' statements will all
branch to the first label after the current program counter which
matches the search string. A 'goto .linenum' command has no such
ambiguity, as all line numbers are necessarily unique.
.PP
The return address stack is flushed out when any one of the following
occurs:
.IP "1)"
the user enters 'prog' mode and deletes or adds a step.
.IP "2)"
the user issues a 'load' command which changes program memory.
.IP "3)"
the user isses a 'run' or 'step' command with a label. If no label
is given the stack is NOT cleared.
.PP
If the return stack is cleared a subroutine or two deep into your
program you will not be able to resume it and expect it to run to
completion. The next 'rtn' statement, instead of returning to the
calling gosub, will return to user mode instead.
.PP
When writing functions which might be used as subroutines later,
take care to choose variable names which are unlikely to collide with
those of the caller, for instance by appending to all names a string
identifying the function of the module. If I implement name scoping at
some future time this won't be so critical. If the function modifies
the I register, that value should be saved on entry and restored just
prior to exit, so that the calling function's behaviour is not
affected.
.PP
The R/S command can be used as a breakpoint. Insert it in the
program, and execution will halt when it hits that line, then you can
step through with the "step" function, or press "run" and the program
will continue from the point following the breakpoint. Also, by
putting R/S immediately after a decision command like "x>y", "f?", or
"x==0" you can make conditional breakpoints.

.SH "MAGIC TO KEEP IN MIND"
.PP
The memory label "(i)" (without the quotation marks) is special. Any
attempt to assign to it or read from it results, instead, in access to
the memory element whose label is the string representation of the
integer value of the I register. To change or retrieve the value of
the I register, the sti or rci functions must be used.
.PP
Similarly, the goto label "(i)" and flag "(i)" are special, see above.
.PP
The statistical functions update special named memory elements, see
the description of "stat+".
.PP
Labels cannot begin with a '.'
.PP
The '#' character begins a comment.
.PP
Flags can be made to clear on test.

.SH FILES
.TP
$HOME/.hp67rc
This file,
if it exists, is read in as a preloaded program file, unless
overridden by command line arguments or the
.Ev HP67PROGRAM
environment variable.

.SH ENVIRONMENT
.TP
HP67PROGRAM
This variable, if set, contains the name of the program file to read
when the emulator starts up, unless overridden by command line
arguments. If this variable is set, and the
.B $HOME/.hp67rc
file exists, then the latter will be read only if the former does not
resolve to a readable file.

.SH "ABOUT THE PROGRAM"
Version 1.0 completed Feb 11, 1997
.PP
Copyleft GPL 1997 by Christopher Neufeld
.PP
Distribute freely so long as this file is included.
.PP
This program is essentially a re-write of the RPN classic desk
accessory for the Apple ][GS which I released in 1993. If anybody out
there has actually used the CDA version, please let me know.

.SH AUTHOR
Christopher Neufeld
.br
<neufeld@physics.utoronto.ca>
hp67/hp67.h100644    771    144        7605  6436613657  11653 0ustar  neufeldusers/* Top header file for the HP-67 emulator. Defines enumerated types,
 * typedefs, and global variables. */


#ifndef HP67_H
#define HP67_H

#include "config.h"

#include <float.h>

#ifndef CHKMEM
#define CHKMEM(x) do { if ((x) == NULL) { fprintf(stderr, "Memory allocation failure. Aborting.\n"); abort(); } } while (0)
#endif  /* !CHKMEM */

#ifndef TRUE
#define TRUE 1
#endif  /* !TRUE */

#ifndef FALSE
#define FALSE 0
#endif  /* !FALSE */


/* The output display mode. Fixed mode, scientific notation,
 * engineering notation, or hexadecimal */
enum disp_mode { FIX = 0, SCI, ENG, HEX };


/* Angle input modes */
enum ang_mode { DEG = 0, RAD, GRD };



/* The current running mode of the program. */
enum run_mode { IMMED = 0, RUN_PROG, ENTER_PROG, STEPPING };

/* Special actions which might be taken following the execution of a
 * program step */
enum sp_acts { NONE = 0, SKIP_NEXT, TAKE_A_STEP, ERROR, EXECUTE_NOW,
	       GET_DISK_INPUT, WRITE_PROGRAM, WRITE_MEMORY };

/* Key values which are not real key values */
enum more_codes { UNINITIALIZED = -1, VALUE = -2, NO_SUCH_KEY = -3 };

/* Outcome of an attempt at parsing an input line for a numeric value */
enum parse_codes { PARSE_OK = 0, PARSE_BAD };

/* Input method return code indicating that the buffer contains valid
 * text, or the person asked for a scroll up or down of the program
 * buffer. */
enum input_codes { TEXTINPUT = 0, NEXTLINE, PREVLINE, LINETOOLONG, 
		     ENDOFINPUT, DELETELINE, STEPSHORTCUT, REDRAW };

enum outcomes { SUCCESS = 0, FAILURE };


#define COMMENT '#'   /* Character at beginning of comments in input stream */

#define ESC '\e'     /* ESCape character */
#define WHITESPACE " \t"   /* Separates tokens */
#define BLANK ' '    /* Blank character */

#define HEXPREFIX "0x"   /* Prepend on hexadecimal output */

#define META 0x80    /* Add to a regular ASCII character to make a META character */

#define INDIRECT "(i)"   /* When this appears as a label, it means that the value of the I register should be used */

#define I_REGISTER memories[INDIRECT]


#define CLRONTESTSUFFIX " clr"   /* This, after a directive to set a flag, tells the program that the flag will be cleared on a test */

#ifdef STRICT_STATS
#define CLRONLYPRIMARY "primary"  /* This, after a directive to erase memory, tells the program to clear only the primary registers */
#endif  /* STRICT_STATS */

#define MAXDIGS DBL_DIG

#define HASH_RECENT 15   // Maximum number of recently-accessed memory elements which will be held

typedef char intstring[20];   /* Big enough to hold any integer */


#ifdef STRICT_STATS
#define STATS_SUMX        "14"
#define STATS_SUMX2       "15"
#define STATS_SUMY        "16"
#define STATS_SUMY2       "17"
#define STATS_SUMXY       "18"
#define STATS_NPTS        "19"
#else
#define STATS_SUMX        "_stats_sumx"
#define STATS_SUMX2       "_stats_sumx2"
#define STATS_SUMY        "_stats_sumy"
#define STATS_SUMY2       "_stats_sumy2"
#define STATS_SUMXY       "_stats_sumxy"
#define STATS_NPTS        "_stats_n"
#endif


#define LABELSTR "label"  /* This indicates that something is a label */
#define RUNSTR   "run"    /* The string which begins a running program */
#define ENTERSTR "ENTER"  /* The string for the ENTER key */
#define IMMEDMODE "immed" /* The string to enter immediate mode */
#define RUNSTOPSTR "R/S"  /* The string for run/stop */
#define CLEARXSTR "clx"
#define STORESTR "sto"    /* The string for storing to memory */
#define STOREINDIRECTSTR "sti"  /* The string for storing in (i) */
#define STEPSTR "step"


#define PRELOADENVVAR "HP67PROGRAM"   /* Environment variable which
				       * points to a file holding the
				       * preload program. */
#define HOMEENVVAR "HOME"             /* The environment variable
				       * which gives the name of the
				       * home directory of the
				       * user. */
#define RCFILENAME ".hp67rc"          /* The name of the .rc file */


#include "hp_prots.h"


#endif  /* !HP67_H */
hp67/hp67.lsm100644    771    144        1100  6503232151  12154 0ustar  neufeldusersBegin3
Title:          Hewlett-Packard HP-67 emulator
Version:        1.4
Entered-date:   16MAR98
Description:	An ncurses-based, programmable scientific calculator
		with Reverse Polish Notation user interface.
Keywords:       calculator programmable RPN
Author:		neufeld@physics.utoronto.ca (Christopher Neufeld)
Maintained-by:  neufeld@physics.utoronto.ca (Christopher Neufeld)
Primary-site:   sunsite.unc.edu /pub/Linux/apps/math/calc
		189 kB hp67-1.3.tar.gz
Alternate-site: caliban.physics.utoronto.ca /pub/linux
Original-site:  
Platforms:      
Copying-policy: GPL
End
hp67/hp67.xpm100644    771    144       11262  6312331571  12223 0ustar  neufeldusers/* XPM */
static char * hp67_xpm[] = {
"48 87 10 1",
" 	s none c none",
".	c #000000000000",
"X	c #B6DAB6DAB6DA",
"o	c #79E779E70000",
"O	c #79E700000000",
"+	c #FBEE14514103",
"@	c #3CF3FBEE34D3",
"#	c #FFFFFFFF0000",
"$	c #0000FFFFFFFF",
"%	c #3CF3FBEE34D3",
"                                                ",
" .............................................. ",
" .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo......................................ooX. ",
" .Xoo.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOO+O+O++OOOOOO+OOO+++OO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOO+O+O+O+OOOOO+OOOOO+OO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOO+++O++OO+++O+++OOO+OO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOO+O+O+OOOOOOO+O+OOO+OO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOO+O+O+OOOOOOO+++OOO+OO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.ooX. ",
" .Xoo.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.ooX. ",
" .Xoo......................................ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoooo........ooooooooooooooooooo.......ooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo######oo$$$$$$oo@@@@@@oo@@@@@@oo......ooX. ",
" .Xoo######oo$$$$$$oo@@@@@@oo@@@@@@oo......ooX. ",
" .Xoo######oo$$$$$$oo@@@@@@oo@@@@@@oo......ooX. ",
" .Xoo######oo$$$$$$oo@@@@@@oo@@@@@@oo......ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo@@@@@@@@@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@@@@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@@@@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .Xoo@@@@@@@@@@@@@@oo@@@@@@oo@@@@@@oo@@@@@@ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo.XXXXXX.ooo.XXXXXX.ooo.XXXXXX.ooX. ",
" .Xoo%%%%%ooo........ooo........ooo........ooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XooooooooooooooooooooooooooooooooooooooooooX. ",
" .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. ",
" .............................................. ",
"                                                "};
hp67/hp67funcs.cc100644    771    144       56334  6436371506  13065 0ustar  neufeldusers/* The functions which do most of the real work in the calculator */


#include <ctype.h>
#include <math.h>
#include <string.h>

#include "hp67.h"
#include "calcfloat.h"
#include "stack.h"

extern Stack<CalcFloat> fltstack;

#include "hpglobals.h"
#include "flags.h"

Flags flag;

#define SET_LASTX fltstack.lastx = *fltstack.elem_x



/* Beware of calling hp_* functions from within other hp_*
 * functions. The *scratchspace[] variables are global, so there is
 * the possibility of interaction unless you're careful. */


void hp_convert_ang_to_rad(Arithdatatype &angle)
{
  switch(angmode) {
  case RAD:
    return;
    break;
  case DEG:
    *angscratch = M_PI / 180.0;
    angle.times(*angscratch);
    break;
  case GRD:
    *angscratch = M_PI / 200.0;
    angle.times(*angscratch);
    break;
  }
  return;  
}


void hp_convert_ang_from_rad(Arithdatatype &angle)
{
  switch(angmode) {
  case RAD:
    return;
    break;
  case DEG:
    *angscratch = 180.0 / M_PI;
    angle.times(*angscratch);
    break;
  case GRD:
    *angscratch = 200.0 / M_PI;
    angle.times(*angscratch);
    break;
  }
  return;  
}



sp_acts push_scalar(CalcProgMem *pmem)
{
  fltstack.push(*pmem->value);
  return NONE;
}

sp_acts hp_add(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().plus(fltstack.pop()));
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_subtract(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().minus(fltstack.pop()));
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_multiply(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().times(fltstack.pop()));
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_divide(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().div_by(fltstack.pop()));
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_sin(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  hp_convert_ang_to_rad(*scratchspace[0]);
  scratchspace[0]->sin();
  fltstack.push(*scratchspace[0]);
  return NONE;
}

sp_acts hp_cos(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  hp_convert_ang_to_rad(*scratchspace[0]);
  scratchspace[0]->cos();
  fltstack.push(*scratchspace[0]);
  return NONE;
}

sp_acts hp_tan(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  hp_convert_ang_to_rad(*scratchspace[0]);
  scratchspace[0]->tan();
  fltstack.push(*scratchspace[0]);
  return NONE;
}

sp_acts hp_arcsin(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  scratchspace[0]->arcsin();
  hp_convert_ang_from_rad(*scratchspace[0]);
  fltstack.push(*scratchspace[0]);
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_arccos(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  scratchspace[0]->arccos();
  hp_convert_ang_from_rad(*scratchspace[0]);
  fltstack.push(*scratchspace[0]);
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_arctan(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  scratchspace[0]->arctan();
  hp_convert_ang_from_rad(*scratchspace[0]);
  fltstack.push(*scratchspace[0]);
  return NONE;
}

sp_acts hp_sqrt(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().sqrt());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_square(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().square());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_log_e(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().log_e());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_exp_e(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().exp_e());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_log_10(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().log_10());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_exp_10(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().exp_10());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_recip(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().recip());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_power(CalcProgMem *)
{
  SET_LASTX;

  *scratchspace[0] = fltstack.pop();

  fltstack.push(*scratchspace[1] = fltstack.pop().power(*scratchspace[0]));
  if (scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}

sp_acts hp_abs(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(fltstack.pop().abs());
  return NONE;
}

sp_acts hp_factorial(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().factorial());
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}


// Other actions


// A label in the program. Nothing happens.
sp_acts hp_label(CalcProgMem *)
{
  return NONE;
}


// A goto statement
sp_acts hp_goto(CalcProgMem *pmem)
{
  if (pmem->perform_goto() == -1) {
    runmode = IMMED;
    return ERROR;
  }
  return NONE;
}


// A gosub statement
sp_acts hp_gosub(CalcProgMem *pmem)
{
  ProgramSpace *pspace;

  pspace = pmem->myspace;
  if (pspace == NULL)
    pspace = &program;


  pspace->push_retaddr();

  if (pmem->perform_goto() == -1) {
    pspace->pop_retaddr();
    runmode = IMMED;
    return ERROR;
  }
  return NONE;
}


sp_acts hp_run(CalcProgMem *pmem)
{
  if (pmem->argument == NULL ||  
      *pmem->argument == 0) {     // No argument, run from where we are.
    runmode = RUN_PROG;
    return NONE;
  }

  if (pmem->perform_goto() == -1) {
    runmode = IMMED;
    return ERROR;
  }

  program.clear_return_stack();

  runmode = RUN_PROG;
  return NONE;
}



// A return statement
sp_acts hp_return(CalcProgMem *pmem)
{
  if (pmem->myspace->pop_retaddr() == -1) {   /* No return address. Done. */
    runmode = IMMED;
    return NONE;
  }

  return NONE;
}


// Change the number of digits to display
sp_acts hp_dispn(CalcProgMem *pmem)
{
  int n;

  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    n = I_REGISTER.int_value();
  } else {
    n = atoi(pmem->argument);
  }


  if (n < 0 || n >= MAXDIGS) {
    runmode = IMMED;
    return ERROR;
  }

  dispdigs = n;
  return NONE;
}


// Change the display mode
sp_acts hp_fix(CalcProgMem *)
{
  dispmode = FIX;
  return NONE;
}


sp_acts hp_sci(CalcProgMem *)
{
  dispmode = SCI;
  return NONE;
}


sp_acts hp_eng(CalcProgMem *)
{
  dispmode = ENG;
  return NONE;
}


sp_acts hp_hexmode(CalcProgMem *)
{
  dispmode = HEX;
  return NONE;
}


// Exchange x and i registers
sp_acts hp_x_int_i(CalcProgMem *)
{
  *scratchspace[0] = I_REGISTER;
  I_REGISTER = fltstack.pop();
  fltstack.push(*scratchspace[0]);
  return NONE;
}


// Not done yet
sp_acts hp_round(CalcProgMem *)
{
  char obuffer[1000];
  
  *scratchspace[0] = fltstack.pop();
  scratchspace[0]->printvalue(dispdigs, dispmode, obuffer, 1000, NULL, NULL);
  *scratchspace[0] = atof(obuffer);
  fltstack.push(*scratchspace[0]);
  return NONE;
}


// Store in memory
sp_acts hp_store(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    *scratchspace[0] = fltstack.reveal_value(0);
    memories[obuf] = *scratchspace[0];
    return NONE;
  } else {
    *scratchspace[0] = fltstack.reveal_value(0);
    memories[pmem->argument] = *scratchspace[0];
    return NONE;
  }
}


sp_acts hp_store_plus(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[obuf].plus(*scratchspace[0]);
  } else {
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[pmem->argument].plus(*scratchspace[0]);
  }
  if (scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_store_minus(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[obuf].minus(*scratchspace[0]);
  } else {
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[pmem->argument].minus(*scratchspace[0]);
  }
  if (scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_store_times(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[obuf].times(*scratchspace[0]);
  } else {
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[pmem->argument].times(*scratchspace[0]);
  }
  if (scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_store_divide(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[obuf].div_by(*scratchspace[0]);
  } else {
    *scratchspace[0] = fltstack.reveal_value(0);
    *scratchspace[1] = memories[pmem->argument].div_by(*scratchspace[0]);
  }
  if (scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_store_i(CalcProgMem *)
{
  I_REGISTER = fltstack.pop();
  fltstack.push(I_REGISTER);
  return NONE;
}


sp_acts hp_dsz(CalcProgMem *)
{
  *scratchspace[0] = 1;
  I_REGISTER.minus(*scratchspace[0]);
  if (I_REGISTER.int_value() == 0)
    return SKIP_NEXT;
  return NONE;
}


sp_acts hp_dsz_indirect(CalcProgMem *)
{
  intstring obuf;

  *scratchspace[0] = 1;
  sprintf(obuf, "%d", I_REGISTER.int_value());
  memories[obuf].minus(*scratchspace[0]);
  if (memories[obuf].int_value() == 0)
    return SKIP_NEXT;

  return NONE;
}



sp_acts hp_recall(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {  /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    fltstack.push(memories[obuf]);
    return NONE;
  } else {
    fltstack.push(memories[pmem->argument]);
    return NONE;
  }
}


sp_acts hp_recall_i(CalcProgMem *)
{
  fltstack.push(I_REGISTER);
  return NONE;
}


sp_acts hp_isz(CalcProgMem *)
{
  *scratchspace[0] = 1;
  I_REGISTER.plus(*scratchspace[0]);
  if (I_REGISTER.int_value() == 0)
    return SKIP_NEXT;
  return NONE;
}


sp_acts hp_isz_indirect(CalcProgMem *)
{
  intstring obuf;

  *scratchspace[0] = 1;
  sprintf(obuf, "%d", I_REGISTER.int_value());
  memories[obuf].plus(*scratchspace[0]);
  if (memories[obuf].int_value() == 0)
    return SKIP_NEXT;

  return NONE;
}



// Change the angle units
sp_acts hp_deg(CalcProgMem *)
{
  angmode = DEG;
  return NONE;
}


sp_acts hp_rad(CalcProgMem *)
{
  angmode = RAD;
  return NONE;
}


sp_acts hp_grad(CalcProgMem *)
{
  angmode = GRD;
  return NONE;
}


// Change the sign
sp_acts hp_chgsgn(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(fltstack.pop().times(*scratchspace[0] = -1));
  return NONE;
}


sp_acts hp_clearx(CalcProgMem *)
{
  (void) fltstack.pop();
  return NONE;
}


// Some logical operators
sp_acts hp_x_not_zero(CalcProgMem *)
{
  if (*fltstack.elem_x != 0)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_is_zero(CalcProgMem *)
{
  if (*fltstack.elem_x == 0)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_lt_zero(CalcProgMem *)
{
  if (*fltstack.elem_x < 0)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_gt_zero(CalcProgMem *)
{
  if (*fltstack.elem_x > 0)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_eq_y(CalcProgMem *)
{
  if (*fltstack.elem_x == *fltstack.elem_y)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_neq_y(CalcProgMem *)
{
  if (*fltstack.elem_x != *fltstack.elem_y)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_leq_y(CalcProgMem *)
{
  if (*fltstack.elem_x <= *fltstack.elem_y)
    return NONE;
  return SKIP_NEXT;
}


sp_acts hp_x_gt_y(CalcProgMem *)
{
  if (*fltstack.elem_x > *fltstack.elem_y)
    return NONE;
  return SKIP_NEXT;
}



// More stack operations
sp_acts hp_x_int_y(CalcProgMem *)
{
  fltstack.exchange_xy();
  return NONE;
}


// Some conversions
sp_acts hp_rec2polar(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  *scratchspace[1] = fltstack.pop();

  scratchspace[0]->rec2polar(scratchspace[0], scratchspace[1]);
  hp_convert_ang_from_rad(*scratchspace[1]);
  fltstack.push(*scratchspace[1]);
  fltstack.push(*scratchspace[0]);
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_polar2rec(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  *scratchspace[1] = fltstack.pop();
  hp_convert_ang_to_rad(*scratchspace[1]);

  scratchspace[0]->polar2rec(scratchspace[0], scratchspace[1]);
  fltstack.push(*scratchspace[1]);
  fltstack.push(*scratchspace[0]);
  return NONE;
}


sp_acts hp_deg2rad(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  *scratchspace[1] = M_PI / 180;
  scratchspace[0]->times(*scratchspace[1]);
  fltstack.push(*scratchspace[0]);
  return NONE;
}


sp_acts hp_rad2deg(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  *scratchspace[1] = 180 / M_PI;
  scratchspace[0]->times(*scratchspace[1]);
  fltstack.push(*scratchspace[0]);
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_h2hms(CalcProgMem *)
{
  int negative;
  int hours, minutes, seconds;

  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  negative = (*scratchspace[0] < 0 ? 1 : 0);
  if (negative) scratchspace[0]->times(*scratchspace[3] = -1);

  hours = scratchspace[0]->int_value();

  scratchspace[0]->minus(*scratchspace[1] = hours);
  scratchspace[0]->times(*scratchspace[1] = 60);
  minutes = scratchspace[0]->int_value();

  scratchspace[0]->minus(*scratchspace[1] = minutes);
  scratchspace[0]->times(*scratchspace[1] = 60);
  seconds = scratchspace[0]->int_value();

  *scratchspace[0] = hours + minutes / 100.0 + seconds / 10000.0;
  if (negative) scratchspace[0]->times(*scratchspace[1] = -1);
  fltstack.push(*scratchspace[0]);
  return NONE;
}



sp_acts hp_hms2h(CalcProgMem *)
{
  int negative;
  int hours, minutes, seconds;

  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  negative = (*scratchspace[0] < 0 ? 1 : 0);
  if (negative) scratchspace[0]->times(*scratchspace[1] = -1);

  hours = scratchspace[0]->int_value();
 
  scratchspace[0]->minus(*scratchspace[1] = hours);
  scratchspace[0]->times(*scratchspace[1] = 100);
  minutes = scratchspace[0]->int_value();

  scratchspace[0]->minus(*scratchspace[1] = minutes);
  scratchspace[0]->times(*scratchspace[1] = 100);
  seconds = scratchspace[0]->int_value();

  *scratchspace[0] = hours + minutes / 60.0 + seconds / 3600.0;

  if (negative) scratchspace[0]->times(*scratchspace[1] = -1);
  fltstack.push(*scratchspace[0]);
  if (scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_pival(CalcProgMem *)
{
  fltstack.push(*scratchspace[0] = M_PI);
  return NONE;
}



// Add two numbers written as hhh.mmss
sp_acts hp_hmsplus(CalcProgMem *)
{
  SET_LASTX;
  (void) hp_hms2h(NULL);
  (void) hp_x_int_y(NULL);
  (void) hp_hms2h(NULL);
  (void) hp_add(NULL);
  return hp_h2hms(NULL);
}


sp_acts hp_intpart(CalcProgMem *)
{
  SET_LASTX;
  fltstack.push(*scratchspace[0] = fltstack.pop().int_value());
  return NONE;
}


sp_acts hp_fracpart(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = fltstack.pop();
  scratchspace[0]->minus(*scratchspace[1] = scratchspace[0]->int_value());
  fltstack.push(*scratchspace[0]);
  return NONE;
}


sp_acts hp_percent(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = *fltstack.elem_x;
  *scratchspace[1] = *fltstack.elem_y;
  scratchspace[0]->times(*scratchspace[1]);
  scratchspace[0]->div_by(*scratchspace[2] = 100);
  (void) fltstack.pop();
  fltstack.push(*scratchspace[0]);
  return NONE;
}


sp_acts hp_pctchange(CalcProgMem *)
{
  SET_LASTX;
  *scratchspace[0] = *fltstack.elem_x;
  *scratchspace[1] = *fltstack.elem_y;
  scratchspace[0]->minus(*scratchspace[1]);
  scratchspace[0]->div_by(*scratchspace[1]);
  (void) fltstack.pop();
  fltstack.push(*scratchspace[0]);
  if (scratchspace[0]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_setflag(CalcProgMem *pmem)
{
  char *holdme;
  bool tclr = FALSE;

  holdme = strdup(pmem->argument);
  if (strcmp(&holdme[strlen(holdme) - strlen(CLRONTESTSUFFIX)], CLRONTESTSUFFIX) == 0) {   // Make this a test-cleared flag
    tclr = TRUE;
    holdme[strlen(holdme) - strlen(CLRONTESTSUFFIX)] = 0;
  }

  if (strcmp(holdme, INDIRECT) == 0) {   /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    flag.set_flag(obuf, tclr);
    free(holdme);
    return NONE;
  } else {
    flag.set_flag(holdme, tclr);
    free(holdme);
    return NONE;
  }
}


sp_acts hp_clrflag(CalcProgMem *pmem)
{
  if (strcmp(pmem->argument, INDIRECT) == 0) {   /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    flag.clr_flag(obuf);
  } else {
    flag.clr_flag(pmem->argument);
  }
  return NONE;
}


sp_acts hp_testflag(CalcProgMem *pmem)
{
  bool retval;

  if (strcmp(pmem->argument, INDIRECT) == 0) {   /* Indirect */
    intstring obuf;

    sprintf(obuf, "%d", I_REGISTER.int_value());
    retval = flag.test_flag(obuf);
  } else {
    retval = flag.test_flag(pmem->argument);
  }
  if (retval) return NONE;
  return SKIP_NEXT;
}


// Statistical functions
sp_acts hp_addstats(CalcProgMem *)
{
  SET_LASTX;
  memories[STATS_SUMX].plus(*fltstack.elem_x);
  *scratchspace[0] = *fltstack.elem_x;
  scratchspace[0]->times(*fltstack.elem_x);
  memories[STATS_SUMX2].plus(*scratchspace[0]);
  memories[STATS_SUMY].plus(*fltstack.elem_y);
  *scratchspace[0] = *fltstack.elem_y;
  scratchspace[0]->times(*fltstack.elem_y);
  memories[STATS_SUMY2].plus(*scratchspace[0]);
  *scratchspace[0] = *fltstack.elem_x;
  scratchspace[0]->times(*fltstack.elem_y);
  memories[STATS_SUMXY].plus(*scratchspace[0]);
  memories[STATS_NPTS].plus(*scratchspace[0] = 1);
  (void) fltstack.pop();
  fltstack.push(memories[STATS_NPTS]);
  return NONE;
}


sp_acts hp_subtractstats(CalcProgMem *)
{
  SET_LASTX;
  memories[STATS_SUMX].minus(*fltstack.elem_x);
  *scratchspace[0] = *fltstack.elem_x;
  scratchspace[0]->times(*fltstack.elem_x);
  memories[STATS_SUMX2].minus(*scratchspace[0]);
  memories[STATS_SUMY].minus(*fltstack.elem_y);
  *scratchspace[0] = *fltstack.elem_y;
  scratchspace[0]->times(*fltstack.elem_y);
  memories[STATS_SUMY2].minus(*scratchspace[0]);
  *scratchspace[0] = *fltstack.elem_x;
  scratchspace[0]->times(*fltstack.elem_y);
  memories[STATS_SUMXY].minus(*scratchspace[0]);
  memories[STATS_NPTS].minus(*scratchspace[0] = 1);
  (void) fltstack.pop();
  fltstack.push(memories[STATS_NPTS]);
  return NONE;
}


sp_acts hp_average(CalcProgMem *)
{
  SET_LASTX;
  (void) fltstack.pop();
  (void) fltstack.pop();

  *scratchspace[0] = memories[STATS_SUMY];
  scratchspace[0]->div_by(memories[STATS_NPTS]);
  fltstack.push(*scratchspace[0]);
  *scratchspace[1] = memories[STATS_SUMX];
  scratchspace[1]->div_by(memories[STATS_NPTS]);
  fltstack.push(*scratchspace[1]);
  if (scratchspace[0]->value_ok() &&
      scratchspace[1]->value_ok())
    return NONE;
  else
    return ERROR;
}


sp_acts hp_sdev(CalcProgMem *)
{
  SET_LASTX;
  (void) fltstack.pop();
  (void) fltstack.pop();

  *scratchspace[0] = memories[STATS_SUMY2];
  *scratchspace[1] = memories[STATS_SUMY];
  scratchspace[1]->times(*scratchspace[1]);
  scratchspace[1]->div_by(memories[STATS_NPTS]);
  scratchspace[0]->minus(*scratchspace[1]);
  *scratchspace[1] = memories[STATS_NPTS];
  scratchspace[1]->minus(*scratchspace[2] = 1);
  scratchspace[0]->div_by(*scratchspace[1]);
  scratchspace[0]->sqrt();
  fltstack.push(*scratchspace[2] = *scratchspace[0]);
  *scratchspace[0] = memories[STATS_SUMX2];
  *scratchspace[1] = memories[STATS_SUMX];
  scratchspace[1]->times(*scratchspace[1]);
  scratchspace[1]->div_by(memories[STATS_NPTS]);
  scratchspace[0]->minus(*scratchspace[1]);
  *scratchspace[1] = memories[STATS_NPTS];
  scratchspace[1]->minus(*scratchspace[2] = 1);
  scratchspace[0]->div_by(*scratchspace[1]);
  scratchspace[0]->sqrt();
  fltstack.push(*scratchspace[0]);
  if (scratchspace[0]->value_ok() &&
      scratchspace[2]->value_ok())
    return NONE;
  else
    return ERROR;
}




#ifdef STRICT_STATS
// This is like the p<>s operation on the HP-67. It exchanges
// the values of memory registers 0 and 10, 1 and 11, 2 and 12, and
// so on up to 9 and 19.
sp_acts hp_exchg_p_s(CalcProgMem *)
{
  int i;
  intstring preg, sreg;

  for (i = 0; i < 10; i++) {
    sprintf(preg, "%d", i);
    sprintf(sreg, "%d", i + 10);
    *scratchspace[0] = memories[preg];
    memories[preg] = memories[sreg];
    memories[sreg] = *scratchspace[0];
  }
  return NONE;
}
#endif  /* STRICT_STATS */


// This function clears whatever registers are in labels "0" to "9"
// and "20" to "24", or all registers if there is no argument of "primary"
#ifdef STRICT_STATS
sp_acts hp_clrreg(CalcProgMem *pmem)
#else
sp_acts hp_clrreg(CalcProgMem *)
#endif
{
  int clr_everything = TRUE;

#ifdef STRICT_STATS
  if (pmem->argument != NULL &&
      pmem->argument[0] != 0 &&
      strcmp(pmem->argument, CLRONLYPRIMARY) == 0)
    clr_everything = FALSE;
#endif  /* STRICT_STATS */  

  if (clr_everything) {
    memories.erase_memory();
  } else {

    int i;
    intstring regname;

    for (i = 0; i < 10; i++) {
      sprintf(regname, "%d", i);
      memories[regname] = 0;
    }

    for (i = 20; i < 25; i++) {
      sprintf(regname, "%d", i);
      memories[regname] = 0;
    }
  }
  return NONE;
}


sp_acts hp_clrstack(CalcProgMem *)
{
  fltstack.erase_stack();
  return NONE;
}


sp_acts hp_clrpmem(CalcProgMem *)
{
  program.erase_program();
  flag.clr_all_flags();
  return NONE;
}



sp_acts hp_enter(CalcProgMem *)
{
  *scratchspace[0] = fltstack.pop();
  fltstack.push(*scratchspace[0]);
  fltstack.push(*scratchspace[0]);
  return NONE;
}


sp_acts hp_prog_mode(CalcProgMem *)
{
  runmode = ENTER_PROG;
  return NONE;
}


sp_acts hp_immed_mode(CalcProgMem *)
{
  runmode = IMMED;
  return NONE;
}


sp_acts hp_step(CalcProgMem *)
{
  runmode = STEPPING;
  return TAKE_A_STEP;
}


sp_acts hp_runstop(CalcProgMem *)
{
  if (runmode == IMMED)
    runmode = RUN_PROG;
  else
    runmode = IMMED;

  return NONE;
}

sp_acts hp_lastx(CalcProgMem *)
{
  fltstack.push(fltstack.lastx);
  return NONE;
}


sp_acts hp_diskinput(CalcProgMem *pmem)
{
  inputstream = fopen(pmem->argument, "r");
  if (inputstream == NULL) return ERROR;
  return GET_DISK_INPUT;
}


sp_acts hp_diskoutput(CalcProgMem *pmem)
{
  outputstream = fopen(pmem->argument, "w");
  if (outputstream == NULL) return ERROR;
  return WRITE_PROGRAM;
}


sp_acts hp_dataoutput(CalcProgMem *pmem)
{
  outputstream = fopen(pmem->argument, "w");
  if (outputstream == NULL) return ERROR;
  return WRITE_MEMORY;
}


sp_acts hp_rolldown(CalcProgMem *)
{
  fltstack.roll_down();
  return NONE;
}


sp_acts hp_rollup(CalcProgMem *)
{
  fltstack.roll_up();
  return NONE;
}

hp67/hp_curses.h100644    771    144        2234  6503230471  13033 0ustar  neufeldusers#ifndef HP_CURSES_H
#define HP_CURSES_H

// Things we need for the ncurses interface.

extern "C" {
#include <curses.h>
}

#ifndef ESC
#define ESC '\e'
#endif


#define TAB '\t'

#define RUBOUT 0177   // Delete key

#define REDRAWKEY 014    // CTRL-L forces a redraw

#define KPSPECIAL1 '\e'           // Keypad numbers might start with this
#define KPSPECIAL2 'O'            // Followed by this
#define KPSPECIAL3 "npqrstuvwxyojmkM"   // Then the character in 3 maps into
#define KPSPECIAL4 ".0123456789/*-+\n"   // the corresponding character in 4

#define OPERATORS "+-*/"     // Special operators, always produce a
                             // carriage return if at beginning of line

#define QUIT 4              // CTRL-D ends input

#define MAX_SIMPLE_CHAR 255

#define NEWLINE 10

#define MOUSE_FLASH_PAUSE 0.05   // 0.05 second pause

#define CURRENTCELLPTR "===>"    // Pointer to the current cell.

#define BARRIERSTR "<<< END OF PROGRAM SPACE >>>"

#define NUMERICPREFIX '{'  // Character which in position 0 indicates that the curses interface should not complete numbers

// #define beep() do { beep() ; wrefresh(mainwin) ; } while (0)

#endif  /* !HP_CURSES_H */
hp67/hp_prots.h100644    771    144        7236  6436616264  12722 0ustar  neufeldusers#ifndef HP_PROTS_H
#define HP_PROTS_H

class CalcProgMem;

sp_acts push_scalar(CalcProgMem *);
sp_acts hp_add(CalcProgMem *);
sp_acts hp_subtract(CalcProgMem *);
sp_acts hp_multiply(CalcProgMem *);
sp_acts hp_divide(CalcProgMem *);
sp_acts hp_sin(CalcProgMem *);
sp_acts hp_cos(CalcProgMem *);
sp_acts hp_tan(CalcProgMem *);
sp_acts hp_arcsin(CalcProgMem *);
sp_acts hp_arccos(CalcProgMem *);
sp_acts hp_arctan(CalcProgMem *);
sp_acts hp_sqrt(CalcProgMem *);
sp_acts hp_square(CalcProgMem *);
sp_acts hp_log_e(CalcProgMem *);
sp_acts hp_exp_e(CalcProgMem *);
sp_acts hp_log_10(CalcProgMem *);
sp_acts hp_exp_10(CalcProgMem *);
sp_acts hp_recip(CalcProgMem *);
sp_acts hp_power(CalcProgMem *);
sp_acts hp_abs(CalcProgMem *);
sp_acts hp_factorial(CalcProgMem *);
sp_acts hp_label(CalcProgMem *);
sp_acts hp_goto(CalcProgMem *);
sp_acts hp_gosub(CalcProgMem *);
sp_acts hp_run(CalcProgMem *);
sp_acts hp_return(CalcProgMem *);
sp_acts hp_dispn(CalcProgMem *);
sp_acts hp_fix(CalcProgMem *);
sp_acts hp_sci(CalcProgMem *);
sp_acts hp_eng(CalcProgMem *);
sp_acts hp_hexmode(CalcProgMem *);
sp_acts hp_x_int_i(CalcProgMem *);
sp_acts hp_round(CalcProgMem *);
sp_acts hp_store(CalcProgMem *);
sp_acts hp_store_i(CalcProgMem *);
sp_acts hp_dsz(CalcProgMem *);
sp_acts hp_dsz_indirect(CalcProgMem *);
sp_acts hp_recall(CalcProgMem *);
sp_acts hp_recall_i(CalcProgMem *);
sp_acts hp_isz(CalcProgMem *);
sp_acts hp_isz_indirect(CalcProgMem *);
sp_acts hp_deg(CalcProgMem *);
sp_acts hp_rad(CalcProgMem *);
sp_acts hp_grad(CalcProgMem *);
sp_acts hp_chgsgn(CalcProgMem *);
sp_acts hp_clearx(CalcProgMem *);
sp_acts hp_x_not_zero(CalcProgMem *);
sp_acts hp_x_is_zero(CalcProgMem *);
sp_acts hp_x_lt_zero(CalcProgMem *);
sp_acts hp_x_gt_zero(CalcProgMem *);
sp_acts hp_x_eq_y(CalcProgMem *);
sp_acts hp_x_neq_y(CalcProgMem *);
sp_acts hp_x_leq_y(CalcProgMem *);
sp_acts hp_x_gt_y(CalcProgMem *);
sp_acts hp_x_int_y(CalcProgMem *);
sp_acts hp_rec2polar(CalcProgMem *);
sp_acts hp_polar2rec(CalcProgMem *);
sp_acts hp_deg2rad(CalcProgMem *);
sp_acts hp_rad2deg(CalcProgMem *);
sp_acts hp_h2hms(CalcProgMem *);
sp_acts hp_hms2h(CalcProgMem *);
sp_acts hp_pival(CalcProgMem *);
sp_acts hp_hmsplus(CalcProgMem *);
sp_acts hp_intpart(CalcProgMem *);
sp_acts hp_fracpart(CalcProgMem *);
sp_acts hp_percent(CalcProgMem *);
sp_acts hp_pctchange(CalcProgMem *);
sp_acts hp_setflag(CalcProgMem *);
sp_acts hp_clrflag(CalcProgMem *);
sp_acts hp_testflag(CalcProgMem *);
sp_acts hp_addstats(CalcProgMem *);
sp_acts hp_subtractstats(CalcProgMem *);
sp_acts hp_average(CalcProgMem *);
sp_acts hp_sdev(CalcProgMem *);
sp_acts hp_exchg_p_s(CalcProgMem *);
sp_acts hp_clrreg(CalcProgMem *);
sp_acts hp_clrpmem(CalcProgMem *);
sp_acts hp_enter(CalcProgMem *);
sp_acts hp_prog_mode(CalcProgMem *);
sp_acts hp_immed_mode(CalcProgMem *);
sp_acts hp_step(CalcProgMem *);
sp_acts hp_runstop(CalcProgMem *);
sp_acts hp_store_plus(CalcProgMem *pmem);
sp_acts hp_store_minus(CalcProgMem *pmem);
sp_acts hp_store_times(CalcProgMem *pmem);
sp_acts hp_store_divide(CalcProgMem *pmem);
sp_acts hp_clrstack(CalcProgMem *);
sp_acts hp_lastx(CalcProgMem *);
sp_acts hp_diskinput(CalcProgMem *pmem);
sp_acts hp_diskoutput(CalcProgMem *pmem);
sp_acts hp_dataoutput(CalcProgMem *pmem);
sp_acts hp_rolldown(CalcProgMem *);
sp_acts hp_rollup(CalcProgMem *);



void append_char(char *string, char c);

int parse_arg_immed_or_clprg(char *rest, char **arg, char **comment);
int parse_arg_goto(char *rest, char **arg, char **comment);
int parse_arg_run(char *rest, char **arg, char **comment);
int parse_arg_loadsave(char *rest, char **arg, char **comment);

void parse_options(int argc, char **argv);
char *get_environment_preload(void);
char *get_rcfile_preload(void);

#endif  /* !HP_PROTS_H */
hp67/hpglobals.h100644    771    144       14555  6436602365  13056 0ustar  neufeldusers#ifndef HPGLOBALS_H
#define HPGLOBALS_H

#ifndef EXTERN
#ifndef NOEXTERN
#define EXTERN extern
#else
#define EXTERN 
#endif  /* !NOEXTERN */
#endif  /* !EXTERN */

#include "progmem.h"
#include "calcfloat.h"
#include "memory.h"
#include "hp_prots.h"


#ifdef NOEXTERN
KeyType *allkeys[] = { 
  new KeyType("+", 0, 1, 1, NULL, NULL, hp_add),
  new KeyType("-", 0, 1, 1, NULL, NULL, hp_subtract),
  new KeyType("*", 0, 1, 1, NULL, NULL, hp_multiply),
  new KeyType("/", 0, 1, 1, NULL, NULL, hp_divide),
  new KeyType("!", 0, 1, 1, NULL, NULL, hp_factorial),
  new KeyType("recip", 0, 1, 1, NULL, NULL, hp_recip),
  new KeyType("chs", 0, 1, 1, NULL, NULL, hp_chgsgn),
  new KeyType("sin", 0, 1, 1, NULL, NULL, hp_sin),
  new KeyType("cos", 0, 1, 1, NULL, NULL, hp_cos),
  new KeyType("tan", 0, 1, 1, NULL, NULL, hp_tan),
  new KeyType("asin", 0, 1, 1, NULL, NULL, hp_arcsin),
  new KeyType("acos", 0, 1, 1, NULL, NULL, hp_arccos),
  new KeyType("atan", 0, 1, 1, NULL, NULL, hp_arctan),
  new KeyType("sqrt", 0, 1, 1, NULL, NULL, hp_sqrt),
  new KeyType("square", 0, 1, 1, NULL, NULL, hp_square),
  new KeyType("ln", 0, 1, 1, NULL, NULL, hp_log_e),
  new KeyType("exp", 0, 1, 1, NULL, NULL, hp_exp_e),
  new KeyType("log10", 0, 1, 1, NULL, NULL, hp_log_10),
  new KeyType("exp10", 0, 1, 1, NULL, NULL, hp_exp_10),
  new KeyType("y^x", 0, 1, 1, NULL, NULL, hp_power),
  new KeyType("abs", 0, 1, 1, NULL, NULL, hp_abs),
  new KeyType("dsp", 1, 1, 1, NULL, NULL, hp_dispn),
  new KeyType("fix", 0, 1, 1, NULL, NULL, hp_fix),
  new KeyType("sci", 0, 1, 1, NULL, NULL, hp_sci),
  new KeyType("eng", 0, 1, 1, NULL, NULL, hp_eng),
  new KeyType("hex", 0, 1, 1, NULL, NULL, hp_hexmode),
  new KeyType("deg", 0, 1, 1, NULL, NULL, hp_deg),
  new KeyType("rad", 0, 1, 1, NULL, NULL, hp_rad),
  new KeyType("grad", 0, 1, 1, NULL, NULL, hp_grad),
  new KeyType(STORESTR, 1, 1, 1, NULL, NULL, hp_store),
  new KeyType("rcl", 1, 1, 1, NULL, NULL, hp_recall),
  new KeyType("sto+", 1, 1, 1, NULL, NULL, hp_store_plus),
  new KeyType("sto-", 1, 1, 1, NULL, NULL, hp_store_minus),
  new KeyType("sto*", 1, 1, 1, NULL, NULL, hp_store_times),
  new KeyType("sto/", 1, 1, 1, NULL, NULL, hp_store_divide),
  new KeyType("x<>y", 0, 1, 1, NULL, NULL, hp_x_int_y),
  new KeyType("r>p", 0, 1, 1, NULL, NULL, hp_rec2polar),
  new KeyType("p>r", 0, 1, 1, NULL, NULL, hp_polar2rec),
  new KeyType("d>r", 0, 1, 1, NULL, NULL, hp_deg2rad),
  new KeyType("r>d", 0, 1, 1, NULL, NULL, hp_rad2deg),
  new KeyType("pi", 0, 1, 1, NULL, NULL, hp_pival),
  new KeyType("h>hms", 0, 1, 1, NULL, NULL, hp_h2hms),
  new KeyType("hms>h", 0, 1, 1, NULL, NULL, hp_hms2h),
  new KeyType("hms+", 0, 1, 1, NULL, NULL, hp_hmsplus),
  new KeyType("int", 0, 1, 1, NULL, NULL, hp_intpart),
  new KeyType("frac", 0, 1, 1, NULL, NULL, hp_fracpart),
  new KeyType("round", 0, 1, 1, NULL, NULL, hp_round),
  new KeyType("rci", 0, 1, 1, NULL, NULL, hp_recall_i),
  new KeyType(STOREINDIRECTSTR, 0, 1, 1, NULL, NULL, hp_store_i),
  new KeyType("dsz", 0, 1, 1, NULL, NULL, hp_dsz),
  new KeyType("dsz(i)", 0, 1, 1, NULL, NULL, hp_dsz_indirect),
  new KeyType("isz", 0, 1, 1, NULL, NULL, hp_isz),
  new KeyType("isz(i)", 0, 1, 1, NULL, NULL, hp_isz_indirect),
  new KeyType("x<>i", 0, 1, 1, NULL, NULL, hp_x_int_i),
  new KeyType("stat+", 0, 1, 1, NULL, NULL, hp_addstats),
  new KeyType("stat-", 0, 1, 1, NULL, NULL, hp_subtractstats),
  new KeyType("avg", 0, 1, 1, NULL, NULL, hp_average),
  new KeyType("sdev", 0, 1, 1, NULL, NULL, hp_sdev),
  new KeyType("%", 0, 1, 1, NULL, NULL, hp_percent),
  new KeyType("%chg", 0, 1, 1, NULL, NULL, hp_pctchange),
#ifdef STRICT_STATS
  new KeyType("p<>s", 0, 1, 1, NULL, NULL, hp_exchg_p_s),
#endif  /* STRICT_STATS */
  new KeyType(CLEARXSTR, 0, 1, 1, NULL, NULL, hp_clearx),
  new KeyType("rdown", 0, 1, 1, NULL, NULL, hp_rolldown),
  new KeyType("rup", 0, 1, 1, NULL, NULL, hp_rollup),
  new KeyType("lastx", 0, 1, 1, NULL, NULL, hp_lastx),
  new KeyType("clstk", 0, 1, 1, NULL, NULL, hp_clrstack),
  new KeyType("clreg", 0, 1, 1, NULL, NULL, hp_clrreg),
  new KeyType("clprg", 0, 0, 1, parse_arg_immed_or_clprg, NULL, hp_clrpmem),
  new KeyType(ENTERSTR, 0, 1, 1, NULL, NULL, hp_enter),
  new KeyType("goto", 1, 1, 1, parse_arg_goto, NULL, hp_goto),
  new KeyType(RUNSTR, 1, 1, 0, parse_arg_run, NULL, hp_run),
  new KeyType(RUNSTOPSTR, 0, 1, 1, NULL, NULL, hp_runstop),
  new KeyType("prog", 0, 1, 0, NULL, NULL, hp_prog_mode),
  new KeyType(STEPSTR, 0, 1, 0, NULL, NULL, hp_step),
  new KeyType(LABELSTR, 1, 0, 1, NULL, NULL, hp_label),
  new KeyType("gosub", 1, 0, 1, NULL, NULL, hp_gosub),
  new KeyType("rtn", 0, 0, 1, NULL, NULL, hp_return),
  new KeyType("x!=0", 0, 0, 1, NULL, NULL, hp_x_not_zero),
  new KeyType("x==0", 0, 0, 1, NULL, NULL, hp_x_is_zero),
  new KeyType("x<0", 0, 0, 1, NULL, NULL, hp_x_lt_zero),
  new KeyType("x>0", 0, 0, 1, NULL, NULL, hp_x_gt_zero),
  new KeyType("x==y", 0, 0, 1, NULL, NULL, hp_x_eq_y),
  new KeyType("x!=y", 0, 0, 1, NULL, NULL, hp_x_neq_y),
  new KeyType("x<=y", 0, 0, 1, NULL, NULL, hp_x_leq_y),
  new KeyType("x>y", 0, 0, 1, NULL, NULL, hp_x_gt_y),
  new KeyType("sf", 1, 1, 1, NULL, NULL, hp_setflag),
  new KeyType("cf", 1, 1, 1, NULL, NULL, hp_clrflag),
  new KeyType("f?", 1, 0, 1, NULL, NULL, hp_testflag),
  new KeyType(IMMEDMODE, 0, 0, 1, parse_arg_immed_or_clprg, NULL, hp_immed_mode),
  new KeyType("r/prog", 1, 1, 0, parse_arg_loadsave, NULL, hp_diskinput),
  new KeyType("w/prog", 1, 1, 0, parse_arg_loadsave, NULL, hp_diskoutput),
  new KeyType("w/data", 1, 1, 0, parse_arg_loadsave, NULL, hp_dataoutput)
};

#else  /* !NOEXTERN */
extern KeyType *allkeys[];
#endif /* !NOEXTERN */

EXTERN KeyInfo *keycaps;   /* The names and qualities of the keys will be kept here */

EXTERN run_mode runmode;

EXTERN int dispdigs;

EXTERN disp_mode dispmode;

EXTERN ang_mode angmode;

#define NUMSCRATCHSPACES 10
EXTERN Arithdatatype *scratchspace[NUMSCRATCHSPACES];
EXTERN Arithdatatype *angscratch;

EXTERN ProgramSpace program;

EXTERN HashTable<CalcFloat> memories;

EXTERN FILE *inputstream, *outputstream;


/* Command line option variables */
EXTERN int noexit;   /* If TRUE, no keypress will cause the program to
		      * exit, it has to be killed by a signal. */
EXTERN char *preload  ;/* If non-NULL, then this points to the
			* filename of a calculator program file to
			* load at startup. */
EXTERN int ignorerc; /* If TRUE, don't read in the .hp67rc file, or
		      * the file in the HP67PROGRAM environment
		      * variable. */


#endif  /* !HPGLOBALS_H */
hp67/hyperbolics.txt100644    771    144        1261  6300147076  13755 0ustar  neufelduserslabel sinh   # The hyperbolic sine of an angle in radians
exp
ENTER
recip
-
2.000000000000000e+000
/
rtn
label cosh   # The hyperbolic cosine of an angle in radians
exp
ENTER
recip
+
2.000000000000000e+000
/
rtn
label tanh   # The hyperbolic tangent of an angle in radians
ENTER
gosub sinh
x<>y
gosub cosh
/
rtn
label asinh   # The inverse hyperbolic sine, in radians
ENTER
square
1.000000000000000e+000
+
sqrt
+
ln
rtn
label acosh   # The inverse hyperbolic cosine, in radians
ENTER
square
1.000000000000000e+000
-
sqrt
+
ln
rtn
label atanh   # The inverse hyperbolic tangent, in radians
ENTER
1.000000000000000e+000
+
x<>y
1.000000000000000e+000
x<>y
-
/
ln
2.000000000000000e+000
/
rtn
hp67/input.cc100644    771    144       50215  6436374770  12377 0ustar  neufeldusers#include <stdio.h>
#include <string.h>
#include <ctype.h>


#define CURSES_INTERFACE

#ifdef CURSES_INTERFACE

extern "C" {
#include <curses.h>
}

#include "hp_curses.h"
#include "input.h"
#include "hpglobals.h"


static char const *numpadkeys[] = { "-", "7", "8", "9", "+", "4", "5", "6",
				    "*", "1", "2", "3", "/", "0", ".", "e" };


ClickableWin::ClickableWin(WINDOW *parent, char const **strings,
			   int nstrings, int fieldw, int numrows, 
			   int numcols, int off_x, int off_y)
{
  int i;

  offset_x = off_x;
  offset_y = off_y;
  nrows = numrows;
  ncols = numcols;
  stringlist = strings;
  numstrings = nstrings;
  fieldwidth = fieldw;
  fieldsperline = numcols / fieldw;

  win = subwin(parent, numrows, numcols, off_y, off_x);
  werase(win);
  
  for (i = 0; i < numstrings; i++)
    write_entry(i);

  wrefresh(win);  
}


CursesIO::CursesIO(KeyInfo const **keyptrs, int nkeys)
{
  int i;
  int maxkeynamelen;

  mainwin = initscr();
  cbreak();
  noecho();
  keypad(mainwin, TRUE);
  meta(mainwin, TRUE);

  can_use_mouse = 0;
  inputwin = stackwin = progwin = memwin = NULL;
  keyswin = numpadwin = NULL;
  credits = NULL;

#if defined ( CURSES_MOUSE ) && NCURSES_MOUSE_VERSION == 1
  if (mousemask(BUTTON1_CLICKED, NULL) == BUTTON1_CLICKED)
    can_use_mouse = 1;
#endif  /* CURSES_MOUSE && NCURSES_MOUSE_VERSION == 1 */

  maxkeynamelen = -1;

  numkeys = nkeys;
  keys = new KeyInfo [nkeys];
  immedstrs = (char const **) malloc(numkeys * sizeof(char const *));
  progstrs = (char const **) malloc(numkeys * sizeof(char const *));
  CHKMEM(immedstrs); CHKMEM(progstrs);
  buffer = NULL;

  for (i = 0; i < nkeys; i++) {
    keys[i] = *keyptrs[i];
    if ((int) strlen(keys[i].keyname) > maxkeynamelen)
      maxkeynamelen = strlen(keys[i].keyname);

    immedstrs[i] = (keys[i].immediate ? keys[i].keyname : (char *) NULL);
    progstrs[i] = (keys[i].program ? keys[i].keyname : (char *) NULL);
  }

  fieldwidth = maxkeynamelen + KEYCAP_PADDING;
  keysperline = COLS / fieldwidth;

  currentview = IMMED_SCRN;
  set_default_immed_view();

  credits = subwin(mainwin, CREDITS_HEIGHT, CREDITS_WIDTH,
		   numpadbottom + 1, numpadleft - 2);
  redraw_credits();
}



CursesIO::~CursesIO(void)
{
  if (buffer != NULL)
    free(buffer);

  free(immedstrs); free(progstrs);
  delete [] keys;
  (void) mousemask(0, NULL);
  destroy_subwindows();
  delwin(mainwin);
  endwin();
}


void CursesIO::draw_numpad(void)
{
  numpadleft = COLS - NUMPAD_WIDTH - 3;
  numpadtop = (numkeys - 1) / keysperline + 2;
  numpadright = numpadleft + NUMPAD_WIDTH;
  numpadbottom = numpadtop + NUMPAD_HEIGHT;

  numpadwin = new ClickableWin(mainwin, numpadkeys, 16, 3, NUMPAD_HEIGHT,
			       NUMPAD_WIDTH, numpadleft, numpadtop);

}


void CursesIO::squeeze_in_memory(void)
{
  if (stackwinsize > 3) {   // only do it if the screen is large enough
    memwinsize = stackwinsize - 2;

    memleft = stackright + HORIZ_SPACE_STACK_TO_MEMORY;
    memright = COLS - (numpadright - numpadleft) - 1 - HORIZ_SPACE_LEFT_OF_NUMPAD;
    memtop = stacktop;
    membottom = stackbottom - MEMORY_SHORTER_THAN_STACK;
    memwin = subwin(mainwin, memwinsize, memright - memleft, memtop, memleft);
  } else {
    memtop = membottom = stacktop;
    memwinsize = memleft = memright = 0;
    memwin = NULL;
  }
}


void CursesIO::set_default_immed_view(void)
{
  destroy_subwindows();

  inputwin = subwin(mainwin, 1, COLS, LINES - DISTANCE_INPUT_FROM_BOTTOM, 0);

  keyswin = new ClickableWin(mainwin, immedstrs, numkeys, fieldwidth,
			     (numkeys - 1) / keysperline + 1, COLS, 0, 0);

  draw_numpad();

  stackleft = 0;
  stacktop = (numkeys - 1) / keysperline + 1 + GAP_BETWEEN_STACK_AND_KEYPAD;
  stackright = MAXDIGS + 9;
  stackbottom = LINES - DISTANCE_INPUT_FROM_BOTTOM - GAP_BETWEEN_X_AND_INPUT;
  stackwinsize = stackbottom - stacktop;

  squeeze_in_memory();

  stackwin = subwin(mainwin, stackwinsize, stackright - stackleft,
		    stacktop, stackleft);

  redraw_credits();
  currentview = IMMED_SCRN;
}


void CursesIO::set_immed_step_view(void)
{
  destroy_subwindows();

  inputwin = subwin(mainwin, 1, COLS, LINES - DISTANCE_INPUT_FROM_BOTTOM, 0);


  keyswin = new ClickableWin(mainwin, immedstrs, numkeys, fieldwidth,
			     (numkeys - 1) / keysperline + 1, COLS, 0, 0);

  draw_numpad();

  stackleft = 0;
  stacktop = (numkeys - 1) / keysperline + 1 + GAP_BETWEEN_STACK_AND_KEYPAD + 1 + STEP_WINDOW_SIZE;
  stackright = MAXDIGS + 9;
  stackbottom = LINES - DISTANCE_INPUT_FROM_BOTTOM - GAP_BETWEEN_X_AND_INPUT;
  stackwinsize = stackbottom - stacktop;

  squeeze_in_memory();

  stackwin = subwin(mainwin, stackwinsize, stackright - stackleft,
		    stacktop, stackleft);

  progleft = PROGRAM_INDENT;
  progtop = stacktop - 1 - STEP_WINDOW_SIZE;
  progright = COLS - (numpadright - numpadleft) - 1 - HORIZ_SPACE_LEFT_OF_NUMPAD;
  progbottom = stacktop - 1;
  progwinsize = progbottom - progtop;

  progwin = subwin(mainwin, progwinsize, progright - progleft,
		   progtop, progleft);

  mvwaddstr(mainwin, progtop + 1, 0, CURRENTCELLPTR);    

  redraw_credits();
  currentview = IMMED_STEP_SCRN;
}


void CursesIO::set_default_prog_view(void)
{
  destroy_subwindows();

  inputwin = subwin(mainwin, 1, COLS, LINES - DISTANCE_INPUT_FROM_BOTTOM, 0);

  keyswin = new ClickableWin(mainwin, progstrs, numkeys, fieldwidth,
			     (numkeys - 1) / keysperline + 1, COLS, 0, 0);

  draw_numpad();

  progleft = PROGRAM_INDENT;
  progtop = (numkeys - 1) / keysperline + 1 + GAP_BETWEEN_PROG_AND_KEYPAD;
  progright = COLS - (numpadright - numpadleft) - 1 - HORIZ_SPACE_LEFT_OF_NUMPAD;
  progbottom = LINES - DISTANCE_INPUT_FROM_BOTTOM - GAP_BETWEEN_PROG_AND_INPUT;
  progwinsize = progbottom - progtop;

  progwin = subwin(mainwin, progwinsize, progright - progleft, progtop,
		   progleft);

  mvwaddstr(mainwin, progtop + (progwinsize + 1) / 2 - 1, 0, CURRENTCELLPTR);

  redraw_credits();
  currentview = PROG_SCRN;
}


void CursesIO::clear_input_line(void)
{
  wmove(inputwin, 0, 0);
  werase(inputwin);
  wrefresh(inputwin);
}


void CursesIO::write_input_line(char const *text)
{
  mvwaddstr(inputwin, 0, 0, text);
  wclrtobot(inputwin);
  wrefresh(inputwin);
}


void CursesIO::append_input_line(char singlechar)
{
  waddch(inputwin, singlechar);
  wrefresh(inputwin);
}


input_codes CursesIO::get_input_line(char *linebuffer, int buffsize)
{
  int linedone;
  int inanumber;
  int inexponent;
  int press;
  int pos;
  int seendecimal;
  static char recovery[5];  // Enough to hold an 'e+'
  int numericcomment = 0;
  int nocompletion = 0;

  linedone = inanumber = inexponent = pos = seendecimal = 0;
  if (dispmode == HEX)
    nocompletion = 1;

  clear_input_line();

  linebuffer[0] = 0;

  if (recovery[0] != 0) {
    strcpy(linebuffer, recovery);

    if (recovery[1] == 0 &&
	strchr(OPERATORS, recovery[0]) != NULL) {
      pos = 0;
      recovery[0] = 0;
      write_input_line(linebuffer);
      return TEXTINPUT;
    }
	
    pos = strlen(recovery);
    write_input_line(linebuffer);
    recovery[0] = 0;
  }

  do {

    press = get_next_char();

    if (press < 0) {
      if (pos != 0) {
	beep();
	continue;  // Special keys only work at start of line
      }

      switch(-press) {
      case ENDOFINPUT:
	linebuffer[0] = 0;
	return ENDOFINPUT;
	break;
      case PREVLINE:
	if (runmode == ENTER_PROG) {
	  linebuffer[0] = 0;
	  return PREVLINE;
	} else {
	  beep();
	  continue;
	}
      case NEXTLINE:
	if (runmode == ENTER_PROG) {
	  linebuffer[0] = 0;
	  return NEXTLINE;
	} else {
	  beep();
	  continue;
	}
      case DELETELINE:
	if (runmode == IMMED) {
	  strcpy(linebuffer, CLEARXSTR);
	  write_input_line(linebuffer);
	  return TEXTINPUT;
	} else {
	  linebuffer[0] = 0;
	  return DELETELINE;
	}
      case STEPSHORTCUT:
	strcpy(linebuffer, STEPSTR);
	write_input_line(linebuffer);
	return TEXTINPUT;
      }
    }

    if (press == NEWLINE && pos != 0) {
      linebuffer[pos] = 0;
      if (nocompletion &&
	  linebuffer[0] == NUMERICPREFIX) {   // strip out the '{' character
	int shiftamt;
	char *cptr = linebuffer;

	shiftamt = 1 + strspn(linebuffer + 1, WHITESPACE);
	do {
	  *(cptr) = *(cptr + shiftamt);
	} while (*(cptr++) != 0);
      }
      return TEXTINPUT;
    }

// The first character on the line will pretty well define the way
// things are going here.
    if (pos == 0) {

      if (press == REDRAWKEY) return REDRAW;
      
      if (press == NUMERICPREFIX) nocompletion = 1;

      if (strchr(OPERATORS, press) != NULL) {
	linebuffer[0] = 0;
	append_char(linebuffer, press);
	return TEXTINPUT;
      }

      if (press == NEWLINE) {
	strcpy(linebuffer, ENTERSTR);
	write_input_line(linebuffer);
	return TEXTINPUT;

      }

      if (press == BLANK || press == RUBOUT)
	continue;   // Silently ignore leading blanks or delete characters

      if (isdigit(press))
	inanumber = 1;

      if (press == '.') {
	inanumber = seendecimal = 1;
	linebuffer[0] = '0';
	append_input_line('0');
	pos = 1;
      }

    } else {


      if (press == RUBOUT) {   // Delete a character
	--pos;
	if (inexponent &&
	    (linebuffer[pos] == 'e' ||
	     linebuffer[pos] == 'E'))
	  inexponent = 0;
	if (seendecimal &&
	    linebuffer[pos] == '.')
	  seendecimal = 0;
	if (numericcomment &&
	    linebuffer[pos] == COMMENT)
	  numericcomment = 0;
      	linebuffer[pos] = 0;
	write_input_line(linebuffer);
	if (pos == 0) {
	  inanumber = 0;
	  nocompletion = 0;
	}
	continue;
      }

      
      if (inanumber && 
	  !nocompletion) {   // The line started with a valid number
	if (! inexponent &&
	    (press == 'e' ||
	     press == 'E')) {   // Exponent character

	  inexponent = 1;

	} else if (press == '.') {

	  if (seendecimal || inexponent)
	    continue;        // Silently ignore extra decimals, as the HP-67

	  seendecimal = 1;
	  
	} else if ((press == '+' || press == '-') &&
		   (linebuffer[pos-1] == 'e' || linebuffer[pos-1] == 'E')) {
	  ;  // Nothing

	} else if (press == COMMENT) {
	  numericcomment = 1;
	} else if (press == BLANK) {
	  ;  // Nothing
	} else if (!isdigit(press) &&
		   !numericcomment) {   // A command is starting
	  int n = 0;

	  if (linebuffer[pos-1] == 'e' || linebuffer[pos-1] == 'E')
	    recovery[n++] = linebuffer[--pos];

	  if (pos > 1 &&
	      (linebuffer[pos-2] == 'e' || linebuffer[pos-2] == 'E') &&
	      (linebuffer[pos-1] == '+' || linebuffer[pos-1] == '-')) {
	    recovery[n++] = linebuffer[pos-2];
	    recovery[n++] = linebuffer[pos-1];
	    pos -= 2;
	  }

	  recovery[n++] = press;
	  recovery[n++] = 0;
	  linebuffer[pos] = 0;
	  write_input_line(recovery);
	  return TEXTINPUT;
	}

      }

    }

    append_input_line(press);
    linebuffer[pos] = press;
    pos++;
    if (pos == buffsize - 1) {
      linebuffer[pos] = 0;
      clear_input_line();
      return TEXTINPUT;
    }

  } while (1);
}



void CursesIO::compute_positions(void)
{
  keysperline = COLS / fieldwidth;
  numkeyrows = (numkeys - 1) / keysperline + 1;
}



int CursesIO::break_run(void) const
{
  nodelay(mainwin, TRUE);
  if (getch() == ERR) {
    nodelay(mainwin, FALSE);
    return FALSE;
  }

  nodelay(mainwin, FALSE);
  return TRUE;
}



// This routine filters inputs. It converts keypad sequences to
// the corresponding single keycap value, interprets the cursor
// keys, and looks for mouse clicks. It discards control characters
// and converts tabs to spaces.
int CursesIO::get_next_char(void)
{
  int press;
  static int nbuffed = 0;
  int buffsize;
  static char *nextp = NULL;
  char *cp1;
  const char kp_special_3[] = KPSPECIAL3;
  const char kp_special_4[] = KPSPECIAL4;


// Allocate a buffer
  if (buffer == NULL) {
    buffsize = ((fieldwidth + 2 > (int) strlen(RUNSTR) + 6) ? 
		fieldwidth + 2 :
		strlen(RUNSTR) + 6);
    buffer = (char *) malloc(buffsize);
    CHKMEM(buffer);
    nbuffed = 0;
    nextp = NULL;
  }

// If the buffer is not empty, send the next character.
  if (nbuffed != 0) {
    press = *nextp;
    nextp++;
    nbuffed--;
    return press;
  }

// Loop until return

  do {

// Get a key from the keyboard
    press = get_a_key();

// If that key was the sequence which begins a keypad sequence, wait for
// the next key.

// Special case if the same key announces a meta key as a keypad key.
#if KPSPECIAL1 == ESC
    if (press == (KPSPECIAL2 | META)) {  // Has first two characters of keypad key
      press = get_a_key();
    
      if ((cp1 = strchr(kp_special_3, press)) != NULL) {  // it's in the list #3
	press = kp_special_4[cp1 - kp_special_3];
      } else {     // It's not in the list, make it "run O"

	if (!isprint(KPSPECIAL2)) { // Can't make it into a run command
	  beep();
	  continue;   // Try again from the top
	}

	strcpy(buffer, RUNSTR);
	append_char(buffer, ' ');
	append_char(buffer, KPSPECIAL2);
	append_char(buffer, NEWLINE);
	press = buffer[0];
	nextp = &buffer[1];
	nbuffed = strlen(buffer) - 1;
      }     
      return press;
    }

#else  /* KPSPECIAL1 != ESC */
    if (press == KPSPECIAL1) {
      press = get_a_key();
      if (press != KPSPECIAL2) {   /* Not a keypad key */
	if (!isprint(KPSPECIAL1)) {
	  beep();
	  continue;
	} else {
	  if (isprint(press)) {
	    buffer[0] = press;
	    nextp = &buffer[0];
	    nbuffed = 1;
	  } else {
	    beep();
	  }
	  return KPSPECIAL1;
	}
      } else {    // Keypad key? First two characters are right for it
	press = get_a_key();

	if ((cp1 = strchr(kp_special_3, press)) != NULL) {  // it's in the list #3
	  press = kp_special_4[cp1 - kp_special_3];
	  return press;
	} else {     // It's not in the list, must be an error
	  beep();
	  continue;
	}
      }
    }

#endif  /* KPSPECIAL != ESC */

// We can only get here if the system has decided that it didn't
// just see a composite keypad character (one composed of three
// returned numbers in rapid succession)
      

    if (press <= MAX_SIMPLE_CHAR &&
	press & META) {
      press &= ~META;

      if (press == BLANK) return -STEPSHORTCUT;

      if (!isprint(press)) {
	beep();
	continue;
      }
      
      if (runmode == ENTER_PROG)
	strcpy(buffer, LABELSTR " ");
      else
	strcpy(buffer, RUNSTR " ");

      append_char(buffer, press);
      append_char(buffer, NEWLINE);
      press = buffer[0];
      nextp = &buffer[1];
      nbuffed = strlen(buffer) - 1;
      return press;
    }
      

    if (press == KEY_A1) return '7';
    if (press == KEY_A3) return '9';
    if (press == KEY_B2) return '5';
    if (press == KEY_C1) return '1';
    if (press == KEY_C3) return '3';
    if (press == KEY_ENTER || press == NEWLINE) return NEWLINE;

// Check for special editing mode sequences, and if received, return
// the negative value of the corresponding enum.
    if (press == KEY_UP) return -PREVLINE;
    if (press == KEY_DOWN) return -NEXTLINE;
    if (press == QUIT) return -ENDOFINPUT;
    if (press == RUBOUT || press == KEY_DC) return -DELETELINE;
    

#if defined ( CURSES_MOUSE ) && NCURSES_MOUSE_VERSION == 1

// Now, check for a mouse click.
    if (press == KEY_MOUSE) {
      MEVENT place;
      char const *textclicked;
      int keynum;

      if (getmouse(&place) == OK) {
	
	if ((textclicked = keyswin->string_clicked(place.x, place.y, &keynum)) != NULL) {
	  strcpy(buffer, textclicked);
	  if (keys[keynum].can_have_arg) {
	    append_char(buffer, BLANK);
	  } else {
	    if (!(textclicked[1] == 0 &&
		  strchr(OPERATORS, textclicked[0]) != NULL))
	      append_char(buffer, NEWLINE); /* not after operators */
	  }

	  nextp = &buffer[1];
	  nbuffed = strlen(buffer) - 1;
	  return buffer[0];
	} else if ((textclicked = numpadwin->string_clicked(place.x, place.y, &keynum)) != NULL) {
	  if (isdigit(*textclicked) || *textclicked == '.') {  // part of number
	    return *textclicked;
	  } else {         // Function key.
	    nbuffed = 0;
	    return *textclicked;
	  }
	} else {
	  beep();
	  wrefresh(inputwin);
	  continue;
	}

      }

    }

#endif  /* CURSES_MOUSE && NCURSES_MOUSE_VERSION == 1 */


// OK, that covers just about everything. Look for a regular key

    if (press == TAB) press = BLANK;

    if (press == KEY_BACKSPACE) return RUBOUT;

    if (press == REDRAWKEY) return REDRAWKEY;

    if (!isprint(press)) {
      beep();
      wrefresh(inputwin);
      continue;
    }

    return press;

  } while (1);
}



void CursesIO::show_stack(VStack &from)
{
  if (currentview != IMMED_SCRN)
    set_default_immed_view();
  draw_stack_elements(from);
  draw_memory_elements();
  refresh();
}


void CursesIO::pretty_draw(WINDOW *win, char *string)
{
  char *eptr, *mantissa;
  int nblanks;

  if (string[0] != '-') {
    waddch(win, BLANK);   // leave blank in sign position
    mantissa = &string[0];
  } else {
    waddch(win, '-');     // put in the minus sign
    mantissa = &string[1];
  }

  if (strstr(mantissa, HEXPREFIX) != NULL ||
      (eptr = strchr(mantissa, 'e')) == NULL) {
    waddstr(win, mantissa);
  } else {     // Has an 'e' in it, break up like the HP-67 display
    *(eptr++) = 0;
    if (*eptr == '+') *eptr = BLANK;
    waddstr(win, mantissa);
    for (nblanks = MAXDIGS + 2 - strlen(mantissa); nblanks > 0; nblanks--)
      waddch(win, BLANK);
    waddstr(win, eptr);
  }
  
}


void CursesIO::draw_stack_elements(VStack &from)
{
  unsigned int i;
  unsigned int stop;
  char ostring[1000];
  int rows, cols;

  stop = from.get_size();
  if (stackwinsize < stop) stop = stackwinsize;

  werase(stackwin);
  for (i = 0; i < stop; i++) {
    from.reveal_value(i).printvalue(dispdigs, dispmode, ostring, 1000,
				    &rows, &cols);
    wmove(stackwin, stackwinsize - i - 1, 0);
    pretty_draw(stackwin, ostring);
  }

  wrefresh(stackwin);
}


void CursesIO::show_stack_and_step(VStack &from, CalcProgMem const *pmem, int pnum)
{
  if (currentview != IMMED_STEP_SCRN)
    set_immed_step_view();

  draw_stack_elements(from);
  draw_memory_elements();
  draw_program_elements(pmem, pnum);
  refresh();
}


void CursesIO::show_prog_space(CalcProgMem const *pmem, int pnum)
{
  if (currentview != PROG_SCRN)
    set_default_prog_view();

  draw_program_elements(pmem, pnum);
  refresh();
}


void CursesIO::draw_memory_elements(void)
{
  unsigned int i;
  Arithdatatype *memval;
  char ostring[1000];
  int rows, cols;
  char const *label;

  if (memwin == NULL) return;

  werase(memwin);
  for (i = 0; i < memwinsize; i++) {
    if ((memval = memories.recent_access(i, &label)) == NULL) break;
    memval->printvalue(dispdigs, dispmode, ostring, 1000, &rows, &cols);
    wmove(memwin, memwinsize - i - 1, 0);
    wprintw(memwin, "%*.*s = ", memright - memleft - MAXDIGS - 13,
	    memright - memleft - MAXDIGS - 13, label);
    pretty_draw(memwin, ostring);
  }
  wrefresh(memwin);
}


void CursesIO::draw_program_elements(CalcProgMem const *pmem, int pnum)
{
  char *buff;
  int bufflen = progright - progleft + 1;
  CalcProgMem const *pptr;
  int yval;
  intstring lnstring;   // line number
  int stepnum = pnum;


  werase(progwin);

  buff = (char *) malloc(bufflen);
  CHKMEM(buff);
 
// First, print the current line. If it's an invalid cell it's because
// the current pointer is at one of the "barrier" program elements
  wmove(progwin, yval = (progwinsize + 1) / 2 - 1, 0);
  sprintf(lnstring, "%03d: ", stepnum);
  waddstr(progwin, lnstring);
  if (pmem->validcell == 0) {
    waddstr(progwin, BARRIERSTR);
  } else {
    pmem->printcontents(buff, bufflen);
    buff[progright - progleft - 6] = 0;  // truncate the line if it's too long
    waddstr(progwin, buff);
  }

// Now, start backing up, examining those cells earlier than the current 
  pptr = pmem->get_prev_mem();
  yval--;
  while (pptr != NULL && yval >= 0) {
    wmove(progwin, yval, 0);
    sprintf(lnstring, "%03d: ", --stepnum);
    waddstr(progwin, lnstring);
    if (pptr->validcell == 0) {
      waddstr(progwin, BARRIERSTR);
    } else {
      pptr->printcontents(buff, bufflen);
      buff[progright - progleft - 6] = 0;  // truncate the line if it's too long
      waddstr(progwin, buff);
    }

    pptr = pptr->get_prev_mem();
    yval--;
  }

// Now, wind forward, examining later cells
  pptr = pmem->get_next_mem();
  yval = (progwinsize + 1) / 2;
  stepnum = pnum;
  while (pptr != NULL && yval < (int) progwinsize) {
    wmove(progwin, yval, 0);
    sprintf(lnstring, "%03d: ", ++stepnum);
    waddstr(progwin, lnstring);
    if (pptr->validcell == 0) {
      waddstr(progwin, BARRIERSTR);
    } else {
      pptr->printcontents(buff, bufflen);
      buff[progright - progleft - 6] = 0;  // truncate the line if it's too long
      waddstr(progwin, buff);
    }

    pptr = pptr->get_next_mem();
    yval++;
  }
  free(buff);
  wrefresh(progwin);
}

#endif  /* CURSES_INTERFACE */
hp67/input.h100644    771    144       12443  6304576421  12231 0ustar  neufeldusers/* Input method implementations */


#ifndef INPUT_H
#define INPUT_H


#define CURSES_INTERFACE

#include <stdio.h>
#include <unistd.h>

extern "C" {
#include <curses.h>
}


#include "hp67.h"
#include "progmem.h"
#include "hp_curses.h"
#include "layout.h"
#include "stack.h"

/* Define input method class for calculator */



class IOmethod {
public:

// The method might want to be informed of the names of calculator buttons
  virtual ~IOmethod(void) { }

  virtual void initialize_io(void) { }  // Initialize

// Get the next input line
  virtual input_codes get_input_line(char *linebuffer, int buffsize) = 0;

// Print the stack
  virtual void show_stack(VStack &from) = 0;
  virtual void show_stack_and_step(VStack &from, 
				   CalcProgMem const *pstep, int pnum) = 0;

// If running a program, do we want to break?
  virtual int break_run(void) const = 0;

// Shutdown function
  virtual void shutdown_io(void) { };
};


#ifdef CURSES_INTERFACE


class ClickableWin {
private:
  WINDOW *win;

  int ncols, nrows;   // The number of rows and columns of this window
  int offset_x, offset_y;  // The offset of the window with respect to the screen
  char const **stringlist;
  int numstrings;
  
  int fieldwidth;
  int fieldsperline;

public:
  ClickableWin(WINDOW *parent, char const **strings, int nstrings, int fieldw,
	       int numrows, int numcols, int off_x, int off_y);

  ~ClickableWin(void) { delwin(win); }

private: 
  int in_my_window(int x, int y) const
    {
      return (x >= offset_x && 
	      x < offset_x + ncols &&
	      y >= offset_y &&
	      y < offset_y + nrows);
    }

  void write_entry(int num) const
    {
      if (stringlist[num] == NULL) return;

      wmove(win, num / fieldsperline, (num % fieldsperline) * fieldwidth);
      waddstr(win, stringlist[num]);
    }

public: 
  char const *string_clicked(int x, int y, int *keynum) const
    {
      if (!in_my_window(x, y)) return NULL;

      x -= offset_x;
      y -= offset_y;

      if (x % fieldwidth > fieldwidth - 1 - KEYCAP_PADDING)
	return NULL;         // Too close to the edge

      *keynum = x / fieldwidth + y * fieldsperline;
      if (*keynum >= numstrings) {
	*keynum = -1;
	return NULL;
      }

      wattron(win, A_REVERSE);
      write_entry(*keynum);
      wrefresh(win);
      usleep((unsigned long) (MOUSE_FLASH_PAUSE * 1000000));
      wattroff(win, A_REVERSE);
      write_entry(*keynum);
      wrefresh(win);

      return stringlist[*keynum];
    }
};


enum curses_shot { IMMED_SCRN = 0, PROG_SCRN, IMMED_STEP_SCRN };


class CursesIO : public IOmethod {
private:
  WINDOW *mainwin;
  int can_use_mouse;

  KeyInfo *keys;
  int numkeys;

  char const **immedstrs;
  char const **progstrs;

  int fieldwidth;
  int keysperline;

  int numkeyrows;


  curses_shot currentview;

  char *buffer;  // An input buffer


  ClickableWin *keyswin;

  int numpadleft, numpadright, numpadtop, numpadbottom;
  ClickableWin *numpadwin;

  WINDOW *inputwin;

  int stackleft, stackright, stacktop, stackbottom;
  unsigned int stackwinsize;
  WINDOW *stackwin;

  int progleft, progright, progtop, progbottom;
  unsigned int progwinsize;
  WINDOW *progwin;
  WINDOW *credits;

  int memleft, memright, memtop, membottom;
  unsigned int memwinsize;
  WINDOW *memwin;

public:
  CursesIO(KeyInfo const **keyptrs, int nkeys);
  ~CursesIO(void);
  input_codes get_input_line(char *linebuffer, int buffsize);
  int break_run(void) const;
  void shutdown_io(void) {};
  void show_stack(VStack &from);
  void show_stack_and_step(VStack &from, CalcProgMem const *pstep, int pnum);
  void show_prog_space(CalcProgMem const *pstep, int pnum);
  void show_appropriate_thing(run_mode runmode, VStack &from, CalcProgMem const *pstep, int pnum)
    {
      switch(runmode) {
      case IMMED:
	show_stack(from);
	break;
      case STEPPING:
	show_stack_and_step(from, pstep, pnum);
	break;
      case ENTER_PROG:
	show_prog_space(pstep, pnum);
	break;
      default:
	;// Nothing
      }
    }
      
private:
  inline void destroy_subwindows(void);
  int get_a_key(void);
  int get_next_char(void);
  void draw_numpad(void);
  void squeeze_in_memory(void);
  void compute_positions(void);
  void clear_input_line(void);
  void write_input_line(char const *text);
  void append_input_line(char singlechar);
  void set_default_immed_view(void);
  void set_default_prog_view(void);
  void set_immed_step_view(void);
  void draw_stack_elements(VStack &from);
  void draw_program_elements(CalcProgMem const *pmem, int pnum);
  void draw_memory_elements(void);
  void pretty_draw(WINDOW *win, char *string);
  void redraw_credits(void) {
    if (credits != NULL) {
      mvwaddstr(credits, 0, 0, "HP67 emulator\n");
      waddstr(credits, "by\n");
      waddstr(credits, "Christopher\n");
      waddstr(credits, "Neufeld");
    }
  }
};



inline void CursesIO::destroy_subwindows(void)
{
  if (inputwin != NULL) delwin(inputwin);
  if (stackwin != NULL) delwin(stackwin);
  if (progwin != NULL) delwin(progwin);
  if (memwin != NULL) delwin(memwin);
  inputwin = stackwin = progwin = memwin = NULL;

  if (keyswin != NULL) delete keyswin;
  if (numpadwin != NULL) delete numpadwin;
  keyswin = numpadwin = NULL;
  werase(mainwin);
}


inline int CursesIO::get_a_key(void)
{
  int a;

  if ((a = getch()) == ESC)
    return getch() + META;
  else
    return a;
}



#endif  /* CURSES_INTERFACE */


#endif  /* !INPUT_H */
hp67/integrator.txt100644    771    144        6242  6363206312  13612 0ustar  neufelduserslabel set_fn_number   # Call this with the number of the entry label to the function
sto fn_entry_label_int   # Store in a memory address
rtn
label simpson_int   # Simpson's rule integration. Push lower limit, upper limit, and number of points
sto steps_int   # number of steps to perform
sto holdsteps_int   # This value won't be overwritten
x==0
rtn    # don't run if asked to perform zero steps
clx
sto highlimit_int   # The upper limit of the integration
clx
sto lowlimit_int   # The lower limit of the integration
x<>i
sto callers_i_int   # store the calling routine's I register, if any
x<>i
clx
0.000000000000000e+000
sto integral_int   # initialize to zero
clx
rcl highlimit_int
rcl lowlimit_int
-
rcl steps_int
/
2.000000000000000e+000
/
sto stepsize_int   # distance between function invocations
clx
rcl lowlimit_int
sto current_x_int   # make a current x for f(x)
clx
label mainloop_int
rcl fn_entry_label_int
x<>i
clx
rcl current_x_int
gosub (i)   # run the function being integrated
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
2.000000000000000e+000
*
sto+ integral_int
clx
rcl stepsize_int
sto+ current_x_int
clx
rcl current_x_int
gosub (i)
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
4.000000000000000e+000
*
sto+ integral_int
clx
rcl stepsize_int
sto+ current_x_int
clx
1.000000000000000e+000
sto- steps_int
clx
rcl steps_int
cf notfinished_int
x!=0
sf notfinished_int
clx
f? notfinished_int
goto mainloop_int
rcl lowlimit_int
gosub (i)
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
sto- integral_int   # I overdid it in the loop, take one off
clx
rcl highlimit_int
gosub (i)
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
sto+ integral_int
clx
rcl highlimit_int
rcl lowlimit_int
-
6.000000000000000e+000
/
rcl holdsteps_int
/
sto* integral_int
clx
rcl integral_int
rcl callers_i_int   # restore the caller's I register
x<>i
clx
rtn
label root_int   # Find a zero of the function based on the guess provided
sto trialx_int
clx
rcl fn_entry_label_int
x<>i
sto callers_i2_int
clx
label mainloop_root_int
rcl trialx_int
sto last_trialx_int
gosub (i)
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
rcl trialx_int
gosub deriv_int
/
sto- trialx_int
clx
rcl trialx_int
round
rcl last_trialx_int
round
x==y    # iterate until there's no printable change from last time
goto finished_root_int
clx
clx
goto mainloop_root_int
label finished_root_int
clx
clx
rcl callers_i2_int
x<>i
clx
rcl trialx_int
rtn
label deltasize_int   # delta-x / x to use for differentiating
sto new_delta_int
rtn
label deriv_int   # get the derivative of the function
rcl fn_entry_label_int
x<>i
sto callers_i3_int
clx
rcl new_delta_int
x!=0
goto deriv_setup_int
5.000000000000000e-005 # Default value of delta-x / x
sto new_delta_int
clx
label deriv_setup_int
clx
ENTER
ENTER
ENTER
rcl new_delta_int
*
2.000000000000000e+000
/
+
gosub (i)
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
x<>y
ENTER
rcl new_delta_int
*
2.000000000000000e+000
/
-
gosub (i)
rcl fn_entry_label_int   # in case the function killed I, reload it
x<>i
clx
-
rcl new_delta_int
/
x<>y
/
rtn
hp67/layout.h100644    771    144        2014  6300353132  12344 0ustar  neufeldusers#ifndef LAYOUT_H
#define LAYOUT_H

#define KEYCAP_PADDING 2   // number of spaces between keycap tokens

#define CREDITS_HEIGHT 4
#define CREDITS_WIDTH 14

#define DISTANCE_INPUT_FROM_BOTTOM 1  // take input on the bottom of the screen.

#define GAP_BETWEEN_X_AND_INPUT 1  // A single blank line between bottom of stack and input line

#define GAP_BETWEEN_STACK_AND_KEYPAD 2  // two blank lines below the keypad

#define MEMORY_SHORTER_THAN_STACK 2  // show two fewer memory elements than stack elements

#define HORIZ_SPACE_STACK_TO_MEMORY 3  // gap between stack and memory displays

#define HORIZ_SPACE_LEFT_OF_NUMPAD 4  // gap between memory and numpad

#define PROGRAM_INDENT 5  // program display indented 5 from left margin

#define STEP_WINDOW_SIZE 3  // Size of a stepping window

#define GAP_BETWEEN_PROG_AND_KEYPAD 1  // space between program listing and keypad

#define GAP_BETWEEN_PROG_AND_INPUT 1  // space between program listing and input line

#define NUMPAD_WIDTH  12
#define NUMPAD_HEIGHT  4

#endif  /* !LAYOUT_H */
hp67/main.cc100644    771    144       15062  6437275523  12162 0ustar  neufeldusers#include <math.h>
#include <stdio.h>





#include "calcfloat.h"

#include "stack.h"
#include "memory.h"

#define NOEXTERN

#include "progmem.h"
#include "hpglobals.h"
#include "input.h"


/* A short subroutine to append a single character to a string */
void append_char(char *string, char c)
{
  int len = strlen(string);
  string[len] = c;
  string[len+1] = 0;
}


Stack<CalcFloat> fltstack;


/* This function has to be here to avoid linkage errors. It seems
 * that, since the only manifestations of the Stack<CalcFloat> and
 * HashTable<CalcFloat> classes are in global variables, somehow the
 * destructors defined in the templates are not compiled in. */
int dummy(void)
{
  Stack<CalcFloat> a;
  HashTable<CalcFloat> b;
  return 0;
}
  

#define INBUFFSIZE 2000   /* Should be big enough */

/* The main loop is here */
int main(int argc, char **argv)
{

  CursesIO *dino; /* dino - Data IN Out? No, a left over variable name
		   * from when fred, barney, wilma, and betty were all
		   * used. */
  char buffer[INBUFFSIZE + 1];
  CalcProgMem memory; /* The input will first be parsed into a program
		       * step object, then that object called upon to
		       * do whatever the input required. */
  int i;
  int pnum;
  sp_acts result;
  input_codes res_input;
  int diskinput = 0;
  int userbreak;
  KeyInfo const **caps; /* Keycaps are kept here */


  noexit = 0;
  preload = NULL;
  ignorerc = 0;

  if (argc > 0 &&
      argv[0][0] != 0) {       /* Just in case.... */

    parse_options(argc, argv);
  
    if (preload == NULL &&
	!ignorerc) {  /* No command line argument to preload a function */
      preload = get_environment_preload();
      if (preload == NULL)
	preload = get_rcfile_preload();
    }

    if (preload != NULL) {  /* We have a preload function to grab */
      inputstream = fopen(preload, "r");
      if (inputstream != NULL) { /* Success */
	diskinput = 1;
	runmode = ENTER_PROG;
      }    
    }
  }

/* Set the initial defaults */
  dispdigs = 2;
  angmode = DEG;
  dispmode = FIX;

/* The memory element for parsed input does not belong to any
 * particular program space. */ 
  memory.myspace = NULL;

/* Count the keys */
  int ncaps = sizeof(allkeys) / sizeof(allkeys[0]);

/* The KeyType class elements are unusable until the keys_complete
 * static function has been called. */
  KeyType::keys_complete(allkeys);

/* The IO methods will want to know what the names of the keycaps are,
 * and how many there are. This creates a set of pointers to the
 * keycaps which can then be sent on to the relevent IO method. */
  caps = (KeyInfo const **) malloc(ncaps * sizeof(KeyInfo const *));
  CHKMEM(caps);
  for (i = 0; i < ncaps; i++)
    caps[i] = &allkeys[i]->keycap;

/* Create the curses interface */
  dino = new CursesIO(caps, ncaps);

/* Some operations need scratch space. If the data types are ever
 * generalized, this construction allows the scratch space elements to
 * be used for the new data types, too. */
  for (i = 0; i < NUMSCRATCHSPACES; i++)
    scratchspace[i] = new CalcFloat;
  angscratch = new CalcFloat;

  do {

/* No screen output while collecting programs and instructions from a 
 * disk file */
    if (!diskinput) {
      CalcProgMem const *perform = program.get_currentcell_ptr(&pnum);
      dino->show_appropriate_thing(runmode, fltstack, perform, pnum);
    }

/* If getting input from disk, just read the next line */
    if (diskinput) {

      if (fgets(buffer, INBUFFSIZE, inputstream) == NULL) {  /* EOF or error */
	fclose(inputstream);
	diskinput = 0;
	runmode = IMMED;  /* Shut down the disk read, return to immediate mode */
	continue;
      }
      if (buffer[strlen(buffer) - 1] == '\n')
	buffer[strlen(buffer) - 1] = 0;   // strip the EOL character

      res_input = TEXTINPUT;  /* The special actions, like advancing the program counter or deleting an input line, can never be performed by disk input */

    } else {
      if (runmode == STEPPING) runmode = IMMED;  // change back to IMMED
      
      if (runmode == IMMED || runmode == ENTER_PROG) {  // getting instructions from keyboard
	res_input = dino->get_input_line(buffer, INBUFFSIZE);
	if (runmode == IMMED && res_input == ENDOFINPUT) {
	  if (noexit) beep();
	  else break;  // quit
	}
	if (res_input == REDRAW) {
	  delete dino;
	  dino = new CursesIO(caps, ncaps);  // the easiest way to redraw the screen if it's been resized
	  continue;
	}
      } else 
	res_input = TEXTINPUT;  // a dummy value for running programs
    }


    switch (runmode) {
    case IMMED:
      (void) KeyType::parse_command(buffer, memory);  // put input into a memory element
      if (memory.validcell) {  // if that worked, act on it
	result = memory.do_step();

	if (result == TAKE_A_STEP) {  // the user asked for "step"
	  result = program.take_a_step(); // run the program in memory
	  if (result == SKIP_NEXT) ++program;  // skip the next step if necessary
	} else if (result == ERROR) {
	  diskinput = 0;
	  beep();
	} else if (result == GET_DISK_INPUT && !diskinput) {
	  diskinput = 1;
	  runmode = ENTER_PROG;   // Disk input always starts with programming
	} else if (result == WRITE_PROGRAM && !diskinput) {
	  program.writeoutput(outputstream);
	  fclose(outputstream);
	} else if (result == WRITE_MEMORY && !diskinput) {
	  memories.writeoutput(outputstream);
	  fclose(outputstream);
	}
      } else {
	diskinput = 0;
	beep();
      }
      break;
    case ENTER_PROG:
      switch (res_input) {
      case NEXTLINE:  // down arrow, advance the program counter
	++program;
	break;
      case PREVLINE: // up arrow, move back the PC
	--program;
	break;
      case DELETELINE: // delete key, kill cell under the PC
	program.delete_current_cell();
	break;
      case ENDOFINPUT: // CTRL-D, return to immediate mode
	strcpy(buffer, IMMEDMODE);  // Fall through to the next stage
      default:
	if (KeyType::parse_command(buffer, memory) == EXECUTE_NOW) {
	  memory.do_step();
	} else if (memory.validcell) {
	  program.insert_cell(memory);
	} else {
	  diskinput = 0;
	  beep();
	}
      }
      break;

    case RUN_PROG:  // loop over program elements until program ends or a key is pressed
      while (runmode == RUN_PROG &&
	     !(userbreak = dino->break_run()))
	if ((result = program.take_a_step()) == ERROR) {
	  diskinput = 0;
	  beep();
	  runmode = IMMED;
	} else if (result == SKIP_NEXT) {
	  ++program;
	}

      if (userbreak) {
	runmode = STEPPING;
      } else {
	runmode = IMMED;
      }
      break;
    case STEPPING:
      fprintf(stderr, "Shouldn't happen.\n");
      abort();
      break;
    }
    
  } while (1);

  for (i = 0; i < ncaps; i++)
    delete allkeys[i];

  free(caps);
  delete dino;
  return EXIT_SUCCESS;
}

hp67/makefile100644    771    144        2335  6444254163  12401 0ustar  neufeldusersCXXFLAGS = -DHAVE_LONG_DOUBLE_LIBM -Wall
LDFLAGS = -lm -lncurses -s

OBJS = main.o hp67funcs.o calcfloat.o progmem.o input.o parsers.o arguments.o

STATICOBJS = $(OBJS) /usr/lib/libg++.a /usr/lib/libstdc++.a
STATICLDFLAGS := $(LDFLAGS)

LDFLAGS += -lg++


hp67 : $(OBJS)
	$(CC) $(OBJS) $(LDFLAGS) -o $@

hp67.static : $(STATICOBJS)
	$(CC) $(STATICOBJS) $(STATICLDFLAGS) -o $@

clean :
	rm -f *.o core *~

veryclean : clean
	rm -f hp67 hp67.static

archive : hp67 hp67.static
	$(MAKE) clean
	( cd .. ; \
	  /bin/rm -f hp67/hp67.tar.gz hp67/hp67.tar ; \
	  tar -c -f hp67/hp67.tar --exclude emacinit.el hp67 )
	gzip hp67.tar

main.o:	calcfloat.h datatypes.H hp67.h config.h hp_prots.h stack.h memory.h \
	progmem.h hpglobals.h input.h hp_curses.h

hp67funcs.o:	hp67.h config.h hp_prots.h calcfloat.h datatypes.H stack.h \
	        hpglobals.h progmem.h memory.h flags.h

calcfloat.o:	calcfloat.h datatypes.H hp67.h config.h hp_prots.h

progmem.o:	calcfloat.h datatypes.H hp67.h config.h hp_prots.h progmem.h \
	        hpglobals.h memory.h

input.o:	hp_curses.h input.h hp67.h config.h hp_prots.h progmem.h \
		calcfloat.h datatypes.H stack.h hpglobals.h memory.h layout.h

parsers.o:	progmem.h calcfloat.h datatypes.H hp67.h config.h hp_prots.h

hp67/memory.h100644    771    144       23171  6300354721  12373 0ustar  neufeldusers/* The hash table structure in the HP-67 calculator. */

#ifndef HASHTABLE_H
#define HASHTABLE_H


/* Implementation of a separate chaining hash table for efficient
 * parsing and location of named memory. Acts on NUL-terminated
 * character strings. */


/* Interface is as follows:

   New hash is created, typically, by "new HashTable". This creates a
   hash table with 1021 elements. A single unsigned integer argument
   to the creation will produce a hash table with that number of
   elements. Hash tables can also be initialized from pre-existing
   tables, and can be copied.

   To add an element to the has table, call:
   hashinsert(char *key, DTYPE &value)

   Both fields are copied, so it is not necessary that the storage
   in them remain valid.

   To retrieve a pointer, call:
   getvalue(char *key)
   The return value is the "value" which was used in the insert
   function.

   To delete an element of the has table, call
   hashdelete(char *key)
   The return value is the "value" which was used in the insert
   function.

   The hash table is rehashed whenever the filling exceeds
   "REHASH_CRITFILL", currently two thirds. The rehashing can be
   forced by calling
   rehash()
   which increases the size of the has table by approximately a 
   factor of two, or
   rehash(unsigned int newsize)
   which changes the has table size to "newsize". Note that if the
   resulting table is overfilled, it will be automatically rehashed
   for a larger table size during the reconstruction of the hash
   table, so it is not possible, in practice, to produce a hash table
   which is overfilled and remains so.

 */



#include <string.h>
#include <stdio.h>
#include <builtin.h>

#include "calcfloat.h"

#include "hp67.h"


#define DEFAULT_HASHSIZE 1021

#define REHASH_CRITFILL (2.0 / 3.0)  /* Filling fraction before rehashing */


static unsigned int const newsizes[] = {2011, 4001, 8009, 16001,
				      32003, 64007, 128021 };  /* primes */
static unsigned int const numsizes = sizeof(newsizes) / sizeof(newsizes[0]);



/* For collisions, separate chaining */
template <class DTYPE>
struct hashcell {
  struct hashcell *next;
  char *keyval;
  DTYPE value;
};


template <class DTYPE>
class HashTable {

private:
  unsigned int tablesize;
  struct hashcell<DTYPE> *table;
  unsigned int numhashed;
  DTYPE const zeroval;
  struct hashcell<DTYPE> *recently[HASH_RECENT];

public:
  HashTable(void);
  HashTable(unsigned int mytablesize);
  HashTable(HashTable const &from);

  ~HashTable(void);

  HashTable const &operator= (HashTable const &from);

  void hashinsert(char const *key, DTYPE const &value); /* Puts into hash */
  DTYPE &getvalue(char const *key);  /* Returns value */
  DTYPE hashdelete(char const *key);  /* Returns old value */

  DTYPE &operator[] (char const *key);

  void rehash(void);
  void rehash(unsigned int tablesize);
  DTYPE *memelement(char const *key);
  void erase_memory(void);
  void writeoutput(FILE *ostream) const;
  DTYPE *recent_access(int i, char const **label) const;

private: 
  unsigned int computehash(char const *keyval) const;
  void gentable(unsigned int size);
  void copytable(HashTable const &from);
  void destroytable(HashTable &from);
  struct hashcell<DTYPE> *findcell(char const *key);
};


template <class DTYPE>
HashTable<DTYPE>::HashTable(void)
{
  HashTable::gentable((unsigned int) DEFAULT_HASHSIZE);
  for (int i = 0; i < HASH_RECENT; i++) recently[i] = NULL;
}


template <class DTYPE>
HashTable<DTYPE>::HashTable(unsigned int mytablesize)
{
  HashTable::gentable(mytablesize);
  for (int i = 0; i < HASH_RECENT; i++) recently[i] = NULL;
}


template <class DTYPE>
HashTable<DTYPE>::HashTable(HashTable const &from)
{
  HashTable::gentable(from.tablesize);
  HashTable::copytable(from);
  for (int i = 0; i < HASH_RECENT; i++) recently[i] = NULL;
}


template <class DTYPE>
HashTable<DTYPE>::~HashTable(void)
{
  HashTable::destroytable(*this);
}


template <class DTYPE>
DTYPE &HashTable<DTYPE>::operator[] (char const *key)
{
  int alreadyhere = -1;
  struct hashcell<DTYPE> *retval = findcell(key);

  for (int i = 0; i < HASH_RECENT; i++)
    if (recently[i] == retval) {
      alreadyhere = i;
      break;
    }
      
  if (alreadyhere != -1) {
    for (int i = alreadyhere; i > 0; i--)
      recently[i] = recently[i-1];

  } else {
    for (int i = HASH_RECENT - 1; i > 0; i--)
      recently[i] = recently[i-1];
  }
  return (recently[0] = retval)->value;
}


template <class DTYPE>
HashTable<DTYPE> const &HashTable<DTYPE>::operator= (HashTable const &from)
{
  if (this != &from) {
    HashTable::destroytable(*this);
    HashTable::gentable(from.tablesize);
    HashTable::copytable(from);
  }

  return *this;
}


template <class DTYPE>
void HashTable<DTYPE>::hashinsert(char const *key, DTYPE const &value)
{
  unsigned int hashedval;
  struct hashcell<DTYPE> *newcell;

  if (key == NULL) {
    fprintf(stderr, "Error. Called hashinsert() with a NULL key pointer.\n");
    return;
  }

  hashedval = computehash(key);
  if (table[hashedval].keyval != NULL) {
    newcell = new struct hashcell<DTYPE>;

    newcell->next = table[hashedval].next;
    table[hashedval].next = newcell;
  } else {
    newcell = &(table[hashedval]);
  }

  newcell->value.operator=( value);
  newcell->keyval = strdup(key);
  numhashed++;

  if (numhashed > REHASH_CRITFILL * tablesize &&
      tablesize < ::newsizes[numsizes-1])
    rehash();
}


template <class DTYPE>
inline DTYPE &HashTable<DTYPE>::getvalue(char const *key)
{
  return *memelement(key);
}


template <class DTYPE>
void HashTable<DTYPE>::writeoutput(FILE *ostream) const
{
  unsigned int i;
  struct hashcell<DTYPE> const *hptr;
  char buffer[1000];

  fprintf(ostream, "immed\n");
  for (i = 0; i < tablesize; i++) {
    hptr = &table[i];
    while (hptr != NULL && hptr->keyval != NULL) {
      if (hptr->value != 0) {
	hptr->value.printvalue(MAXDIGS - 1, SCI, buffer, 1000, NULL, NULL);
	fputs(buffer, ostream);
	if (strcmp(hptr->keyval, INDIRECT) == 0) {
	  fprintf(ostream, "\n%s\n%s\n", STOREINDIRECTSTR, CLEARXSTR);
	} else {
	  fprintf(ostream, "\n%s %s\n%s\n", STORESTR, hptr->keyval, CLEARXSTR);
	}
      }
      hptr = hptr->next;
    }
  }
}
  


template <class DTYPE>
DTYPE HashTable<DTYPE>::hashdelete(char const *key)
{
  struct hashcell *ptr;
  struct hashcell *lastp;
  struct hashcell *temp;
  unsigned int index;
  DTYPE retval;

  index = computehash(key);
  ptr = &table[index];
  if (strcmp(ptr->keyval, key) == 0) { /* Found it in one */
    free(ptr->keyval);
    retval = ptr->value;
    if (ptr->next != NULL)
      *ptr = *ptr->next;
      
    return retval;
  }

// If we make it here the value is hiding somewhere in the linked list

  while (ptr != NULL &&
	 strcmp(ptr->keyval, key) != 0) {
    lastp = ptr;
    ptr = ptr->next;
  }

  if (ptr == NULL) 
    return (DTYPE) 0.0;

  free(ptr->keyval);
  retval = ptr->value;
  if (ptr->next != NULL) {
    temp = ptr->next;
    *ptr = *temp;
    free(temp);
  } else {
    lastp->next = NULL;
    free(ptr);
  }

  this->numhashed--;
  return retval;
}


template <class DTYPE>
void HashTable<DTYPE>::rehash(void)
{
  unsigned int i;
  unsigned int newsize;

  newsize = 0;
  for (i = 0; i < numsizes; i++)
    if (::newsizes[i] > tablesize) {
      newsize = ::newsizes[i];
      break;
    }

  if (newsize == 0) {
    fprintf(stderr, "Warning. Unable to rehash.\n");
    return;
  }

  rehash(newsize);
}


template <class DTYPE>
void HashTable<DTYPE>::rehash(unsigned int newsize)
{
  HashTable *acopy;

  acopy = new HashTable(*this);
  destroytable(*this);
  gentable(newsize);
  copytable(*acopy);
  delete acopy;
}


template <class DTYPE>
inline unsigned int HashTable<DTYPE>::computehash(char const *key) const
{
  return hashpjw(key) % tablesize;
}


template <class DTYPE>
void HashTable<DTYPE>::gentable(unsigned int size)
{
  unsigned int i;

  numhashed = 0;
  table = new struct hashcell<DTYPE> [size];
  tablesize = size;

  for (i = 0; i < tablesize; i++) {
    table[i].next = (struct hashcell<DTYPE> *) NULL;
    table[i].value = DTYPE(0);
    table[i].keyval = (char *) NULL;
  }
  for (i = 0; i < HASH_RECENT; i++)
    recently[i] = NULL;
}


template <class DTYPE>
void HashTable<DTYPE>::copytable(HashTable const &from)
{
  unsigned int i;
  struct hashcell<DTYPE> const *hptr;

  for (i = 0; i < from.tablesize; i++) {
    hptr = &from.table[i];
    while (hptr != NULL && hptr->keyval != NULL) {
      hashinsert(hptr->keyval, hptr->value);
      hptr = hptr->next;
    }
  }
}


template <class DTYPE>
void HashTable<DTYPE>::destroytable(HashTable &killme)
{
  unsigned int i;
  struct hashcell<DTYPE> *hptr;

  for (i = 0; i < killme.tablesize; i++)
    while (killme.table[i].next != NULL) {
      hptr = killme.table[i].next;
      free(hptr->keyval);
      killme.table[i].next = hptr->next;
      delete hptr;
    }

  delete [] killme.table;
  killme.table = NULL;
  killme.numhashed = 0;
  killme.tablesize = 0;
}


template <class DTYPE>
struct hashcell<DTYPE> *HashTable<DTYPE>::findcell(char const *key)
{
  struct hashcell<DTYPE> *ptr;

  ptr = &table[computehash(key)];
  while (ptr != NULL &&
	 ptr->keyval != NULL &&
	 strcmp(ptr->keyval, key) != 0)
    ptr = ptr->next;

  if (ptr == NULL || ptr->keyval == NULL) {
    hashinsert(key, (DTYPE)0.0);
    return findcell(key);
  }

  return ptr;
}


template <class DTYPE>
inline DTYPE *HashTable<DTYPE>::memelement(char const *key)
{
  return &(findcell(key)->value);
}


template <class DTYPE>
void HashTable<DTYPE>::erase_memory(void)
{
  destroytable(*this);
  gentable(newsizes[0]);
}

template <class DTYPE>
DTYPE *HashTable<DTYPE>::recent_access(int i, char const **label) const
{
  if (recently[i] == NULL) return NULL;
  *label = recently[i]->keyval;
  return &recently[i]->value;
}


#endif  /* !HASHTABLE_H */

hp67/parsers.cc100644    771    144        6333  6300160467  12663 0ustar  neufeldusers#include "progmem.h"


static void basicparse(char *rest, char **arg, char **comment, int *hasarg)
{
  char *cp2, *cptr;

  *arg = *comment = NULL;
  cp2 = rest + strspn(rest, WHITESPACE); /* Flush initial whitespace */
  if (*cp2 != 0) {  /* There's something in 'rest' */

    *arg = cp2;   /* Arg points to the argument plus
		   * any comment */

    if ((cptr = strchr(*arg, COMMENT)) != NULL) {
      *cptr = 0;
      *comment = cptr + 1;  /* Clip the comment off the string, and
			     * point to it */
    } else {
      *comment = *arg + strlen(*arg);
    }

    cptr = *arg + strlen(*arg) - 1;
    while (strchr(WHITESPACE, *cptr)) *(cptr--) = 0;  // flush trailing whitespace
  }

  *hasarg = (*arg == NULL || **arg == 0) ? 0 : 1;
}


/* Parse everything after the first word in the line to produce
 * argument and comment fields. These are located inside the "rest"
 * argument, a pointer to a NUL-terminated string representing the
 * text after the the first word which was keyed in. After the
 * subroutine returns, the contents of "rest" have been altered 
 *
 * Return values: -1 if the parse was bad
 *                 0 if the parse was good
 *                 1 if the parsing has produced a command which cannot
 *                     be stored as program memory, but must be executed
 *                     immediately even when in program mode.
 */
/* Default function never returns 1. Default function assumes that
 * if an argument is allowed, then it is required. */
int KeyType::parse_arg(char *rest, char **arg, char **comment) const
{
  int hasarg;

  if (this->new_parse_arg != NULL)
    return (*this->new_parse_arg)(rest, arg, comment);

  basicparse(rest, arg, comment, &hasarg);

  if (*arg != NULL && **arg == '.') return -1;   // Arguments must not begin with '.'
  if (hasarg == this->keycap.can_have_arg) return 0;
  return -1;
}


// The following function is not a method of KeyType, but it's related
// to KeyType::parse_arg()
// We need a special parse for "immed" because it cannot be stored as
// a program command.
int parse_arg_immed_or_clprg(char *rest, char **arg, char **comment)
{
  int hasarg;

  basicparse(rest, arg, comment, &hasarg);

  if (hasarg) return -1;

  return 1;
}

int parse_arg_goto(char *rest, char **arg, char **comment)
{
  char *endptr;
  int hasarg;

  basicparse(rest, arg, comment, &hasarg);
  
  if (!hasarg) return -1;    // Problem, 'goto' must have an argument

  if (**arg == '.') {    // Absolute address branch
    (void) strtol(*arg + 1, &endptr, 10);  // Check to make sure it's a valid
    if (*endptr == 0)                      // integer
      return 1;
    else
      return -1;
  }

  return 0;
}


int parse_arg_run(char *rest, char **arg, char **comment)
{
  int hasarg;

  basicparse(rest, arg, comment, &hasarg);


  if (*arg != NULL && **arg == '.') return -1;   // Arguments must not begin with '.'
  if (runmode != IMMED &&
      !hasarg)
    return -1;      // Need an argument if not in immediate mode

  return 0;
}


int parse_arg_loadsave(char *rest, char **arg, char **comment)
{
  char *cptr;

  *arg = rest + strspn(rest, WHITESPACE);
  *comment = *arg + strlen(*arg);
  cptr = *comment - 1;
  while (strchr(WHITESPACE, *cptr)) *(cptr--) = 0;

  if (**arg != 0) return 0;
  return -1;
}
hp67/progmem.cc100644    771    144       15152  6300366125  12670 0ustar  neufeldusers#include "calcfloat.h"
#include "progmem.h"
#include "hpglobals.h"


int KeyType::keycount;
KeyType **KeyType::sortedkeys;



KeyType::KeyType(char const fn_keyname[],
		 int hasanarg,
		 int fn_immediate,
		 int fn_program,
		 int (* const fn_parse_arg)(char *rest, char **arg, char **comment),
		 void (* const fn_print_line)(char *buffer, int blen,
					      char const *arg,
					      char const *comment),
		 sp_acts (* const fn_action)(CalcProgMem *prog))
{
  keycount++;
  keycap.keyname = strdup(fn_keyname);
  keycap.can_have_arg = (hasanarg != 0);
  keycap.immediate = fn_immediate;
  keycap.program = fn_program;

  new_parse_arg = fn_parse_arg;
  new_print_line = fn_print_line;
  new_action = fn_action;
}



void KeyType::print_line(char *buffer, int blen, char const *arg,
			 char const *comment) const
{
  if (this->new_print_line != NULL) {
    (*this->new_print_line)(buffer, blen, arg, comment);
    return;
  }

  if (arg == NULL)
    snprintf(buffer, blen, "%s", this->keycap.keyname);
  else 
    snprintf(buffer, blen, "%-s %-s", this->keycap.keyname, arg);

  buffer[blen-1] = 0;

  if (arg != NULL &&
      comment != NULL && 
      comment[0] != 0 &&
      (int) strlen(buffer) < blen - 2) {
    strcat(buffer, "   ");
    append_char(buffer, COMMENT);
    strncat(buffer, comment, blen - 2 - strlen(buffer));
  }
}


sp_acts KeyType::action(CalcProgMem *prog) const
{
  if (this->new_action != NULL) {
    return (*this->new_action)(prog);
  }

  /* No default action */
  return NONE;
}




void CalcProgMem::copycontents(CalcProgMem *dest, CalcProgMem const &src) const
{

  dest->validcell = src.validcell;

  if (dest->value != NULL) delete dest->value;

  if (src.value != NULL) {
    dest->value = new CalcFloat(*src.value);
  } else {
    dest->value = NULL;
  }

  if (src.argument != NULL) {
    if (dest->argument != NULL)
      free(dest->argument);
    dest->argument = (char *) malloc(strlen(src.argument) + strlen(src.comment) + 2);
    CHKMEM(dest->argument);
    strcpy(dest->argument, src.argument);
    strcpy(dest->argument + strlen(src.argument) + 1, src.comment);  
  } else {
    dest->argument = NULL;
  }

  dest->myspace = src.myspace;
  dest->function = src.function;

  if (src.comment != NULL)
    dest->comment = dest->argument + (src.comment - src.argument);

  
  dest->gototarget = NULL;   // Don't count on the address being good
  dest->prevmem = src.prevmem;
  dest->nextmem = src.nextmem;
}


int CalcProgMem::perform_goto(void)
{
  int indirect = 0;

  if (argument[0] == '.') { // Absolute address
    gototarget = (CalcProgMem *) program.findlinenum(gotolinenum = atoi(argument+1));
    if (gototarget == NULL) return -1;
    return 0;
  }

  if (myspace == NULL) {
    gototarget = (CalcProgMem *) program.findlabel(argument, &indirect);
    if (gototarget == NULL) return -1;
    if (indirect) {
      gototarget = NULL;
    } else {
      gotolinenum = program.currentnum;
      if (runmode == RUN_PROG ||
	  runmode == STEPPING)
	program.must_invalidate_gotos = TRUE;
    }
    return 0;

  } else {

    if (gototarget == NULL) {
      gototarget = (CalcProgMem *) myspace->findlabel(argument, &indirect);
      if (gototarget == NULL) return -1;
      if (indirect) {
	gototarget = NULL;
      } else {
	gotolinenum = program.currentnum;
	if (runmode == RUN_PROG ||
	    runmode == STEPPING)
	  program.must_invalidate_gotos = TRUE;
      }
    } else {
      myspace->currentcell = gototarget;
      myspace->currentnum = gotolinenum;
    }
    
    return 0;
  }
}




// The ProgramSpace functions

CalcProgMem const *ProgramSpace::findlinenum(int linenum)
{
  if (linenum > (int) numcells + 1 || linenum < 0) return NULL;
  if (linenum < abs((int) (currentnum - linenum))) {   // Close to beginning
    currentcell = firstmem;
    currentnum = 0;
    *this += linenum;
  } else if ((int) numcells + 1 - linenum < abs((int) (currentnum - linenum))) { // Close to end
    currentcell = lastmem;
    currentnum = numcells + 1;
    *this -= numcells + 1 - linenum;
  } else {   // Close to current position
    *this += linenum - currentnum;
  }
  return currentcell;  
}



CalcProgMem const *ProgramSpace::findlabel(char const *label, int *indirect)
{
  CalcProgMem const *stop;
  int success;

  if (numcells == 0) {
    currentcell = firstmem;
    return NULL;  // No program, so the label's not there
  }

  if (strcmp(label, INDIRECT) == 0) {  /* An indirect subroutine call */
    int ival = I_REGISTER.int_value();

    *indirect = 1;
    if (ival < 0) {   // Negative number, back up that many steps
      *this -= -ival;
      return currentcell;
    } else {          // Positive number, find the appropriate label
      intstring sbuf;  // Sufficient to hold any integer
      
      sprintf(sbuf, "%d", ival);
      return findlabel(sbuf, indirect);    // Recurse in, doing the indirection
    }
  }

  stop = currentcell;
  success = 0;
  
  do {
    if (currentcell->argument != NULL &&  // A quick check, short-circuit
	*currentcell->argument != 0 &&    // the expensive functions
	strcmp(currentcell->function->keycap.keyname, LABELSTR) == 0 &&
	strcmp(currentcell->argument, label) == 0) { /* Found the label */
      success = 1;
      break;
    }
  
    ++(*this);

  } while (currentcell != stop);

  if (success) return currentcell;  // Found it

  return NULL;   // No such label
}



void ProgramSpace::push_retaddr(void)
{
  ReturnAddr *newframe = new ReturnAddr;
  newframe->retaddr = currentcell;
  newframe->retstepnum = currentnum;
  newframe->nextframe = frame0;
  frame0 = newframe;
}


/* Return from last gosub. If there are no more return frames, send back -1 */
int ProgramSpace::pop_retaddr(void)
{
  ReturnAddr *oldframe;

  if (frame0 == NULL) return -1;

  currentcell = frame0->retaddr;
  currentnum = frame0->retstepnum;
  oldframe = frame0;
  frame0 = frame0->nextframe;
  delete oldframe;
  return 0;
}


void ProgramSpace::erase_program(void)
{
  currentcell = lastmem->prevmem;
  currentnum = numcells;

  while (numcells > 0)
    delete_current_cell();
  clear_return_stack();
}


void ProgramSpace::clear_return_stack(void)
{
  ReturnAddr *holdf;

  while (frame0 != NULL) {
    holdf = frame0;
    frame0 = frame0->nextframe;
    delete holdf;
  }
}



void ProgramSpace::invalidate_goto_ptrs(void)
{
  CalcProgMem *pptr = firstmem->nextmem;

  while (pptr != &barrierhigh) {
    pptr->gototarget = NULL;
    pptr = pptr->nextmem;
  }
  must_invalidate_gotos = FALSE;
}


void ProgramSpace::writeoutput(FILE *ostream)
{
  CalcProgMem *pptr = firstmem->nextmem;
  char buffer[1000];

  while (pptr != lastmem) {
    pptr->printcontents(buffer, 1000);
    fputs(buffer, ostream);
    fprintf(ostream, "\n");
    pptr = pptr->nextmem;
  }
}
hp67/progmem.h100644    771    144       25362  6300427072
Results 1 - 1
Help - FTP Sites List - Software Dir.
Searching half a billion files worldwide
© 1997-2009 MARUHN Internet Solutions