Execution speed is an important consideration in program
development, but the 28 and 48 series don't have a built-in
command for timing execution, and although the 49 series has the
TEVAL command, it leaves a bit to be desired. These programs are
what I use for timing execution.

Credit: My timer programs were originally based on Bill Wickes's
'TIMED' programs, as published in "HP-28 Insights Principles and
Programming of the HP-28C/S" and "HP 48 Insights Part I:
Principles and Programming", but have significant differences from
his programs. Note that the programs have a natural resemblance to
the TEVAL command built-in on the 49 series, but actually predate
that considerably.

These programs are UserRPL, but of course, one could easily enough
make similar SysRPL versions. Feel free to modify them as you see
fit, but give credit where credit is due, including taking credit
for any modifications that you make.

The programs take any object on stack level 1 or the command line
(any arguments that the object requires must also be available in
the appropriate places), and return any results from the object,
followed by the time in ticks (1 tick = 1/8192 second) (Tagged as
"Ticks") on level 2, and the time in seconds (as a unit object),
rounded to 4 decimal places, on level 1, except that on the 28
series, these are both strings. Of course, the typical use for
this would be comparing the execution times of programs or
algebraic objects.

If execution is aborted due to an error while executing the object
to be timed, the times won't be returned.

The source code (including the "%%HP: T(3)A(R)F(.);" ASCII
transfer header) for the 48/49 program may be copied and pasted to
a text file, and the file downloaded to a calculator. For a 28
series, you'll have to key it in manually, unless you've made
hardware modifications to the 28.

With minor modifications, the source code that I present works on
any 48 or 49 series calculator, and with a few more, on any 28
series calculator. I'll list two programs, one for the 48/49
series, and one for the 28 series.

In particular, the trailing decimal points on numbers serve to
make them "real numbers" instead of "exact integers" on the 49
series, and may be omitted if you choose to manually key in the
program on the 48 series. For that matter, it should work to use
exact integers on the 49 series, although I haven't tried that.

I've tested these (modified as needed) on a 28C version 1BB, 28S
version 2BB, 48SX versions D and E, 48GX version R, 49G revisons
1.18 & 1.19-6, 49g+ revision 1.23, and various other 49 series
revisions

An important modification is the value to subtract to compensate
for the ticks used internally in the program between the two TICKS
commands. This varies by model, and from unit to unit within the
same model, and sometimes even on the same unit depending on,
well, I don't know, perhaps battery condition, available memory,
the phase of the moon and positions of the planets....

Anyway, I currently use 96 (as in the source below) on my 49g+,
242 on a 49G, 72 on a 48GX, 104 on a 48SX, 116 on a 28S, and 159
on a 28C. To determine the "best value", with the stack clear,
temporarily disable the various "last" saves by toggling off STK,
ARG, and CMD in the MODES MISC menu (menu 20.02 on the 48S series,
menu 69 on the 48G and 49 series) or CMD, UNDO, and LAST in page 2
of the 28 series MODE menu. Then put a very simple built-in data
class object such as the real number 1 on the stack, and run the
program. Try this a few times, (dropping the resulting times)
noting the "Ticks:" values (which are likely to vary, particularly
with the 49 series) and take an average. If the ticks value
returned is (on average) positive, increase the value in the
program by that amount, or if the value is negative, decrease the
value by that amount. Try again a few more times until you're
satisfied that it returns about zero. The rationale behind this
procedure is that it should take less than a "tick" to execute the
real number 1, so I consider that execution time to be about zero.
With the 28 or 48 series, expect to get 0 within 1 tick
consistently; with the 49 series, you'll be lucky to get within 10
ticks consistently. Don't forget to re-enable the "last" saves
when you're done.

Note that if you have a name on the stack, this program includes
the time that it takes for the calculator to resolve the name and
put its contents on the stack. This may be trivial if the variable
is near the beginning of the current directory, but could be
significant if, for example, the variable is near the end of the
home directory but your current directory is deep within the
directory structure. For this reason, I normally put the object
itself on the stack, and use the timer program on that. If you
prefer, you could add conditional code that checks whether the
object type is a global or local name and recall the contents if
it is. For an object stored in a port, the "name" would be a
tagged object, but I suppose that you could use a RCL command
within an error trap for that case.

Note that the time returned does not include the time required to
actually display the results, so is a bit inaccurate, but should
still be useful for comparing execution times. Also, I chose to
round the time in seconds to four decimal places, but that may be
a bit of a stretch, especially on the 49 series. Since the
execution time may vary, it's best to make more than one trial,
and take an average.

With the 49 series, the time to execute on object increases if you
have many objects on the stack above it; I haven't noticed this
effect in the 48 series, but it's possible. Also, with many
objects on the 49 series stack, the time is longer if you have
last stack saves enabled. I haven't experimented with whether it's
just the number of objects or also the size of objects on the
stack that makes the difference. In any case, for consistent
results, it seems best to not have any unneeded objects on the
stack when using any timer program.

This program uses the MEM command to force a "garbage collection"
(actuallly, memory packing) before starting the timing, in the
expectation that this will usually prevent any garbage collection
from occurring during a timing and thus adding to the time. But of
course, depending on the amount of memory available and the amount
used while executing the object, a garbage collection may still be
needed, which would add to the time.

Note that the built-in TEVAL command (49 series only) is similar,
but makes no attempt to compensate for the time that it uses
internally between the two calls to get the system time. Also,
TEVAL respects the current wordsize when subtracting the starting
system time from the ending system time (52-bit values), so may
truncate the difference to the wordsize. If the wordsize is at
least 52, this doesn't matter, but it makes TEVAL unsuitable when
a small wordsize is required or forced by the object being timed.

Of course, an "@" and anything following on the same line is a
comment, and it would be useless to key it in. "\<<" and "\>>"
represent program delimiters, and "\->" represents the right arrow,
character 141 decimal.

The source code for the 48/49 series follows:


%%HP: T(3)A(R)F(.);
\<<                      @ Begin program.
  MEM                    @ Force garbage collection.
  \-> t                  @ Store MEM result in dummy local var.
  \<<                    @ start local var defining procedure.
    TICKS                @ Starting time.
    't' STO              @ Store in local variable.
    EVAL                 @ EVAL whatever's on level 1.
    TICKS                @ Ending time.
    RCWS                 @ Original wordsize.
    64. STWS             @ Force full 64-bit wordsize.
    SWAP                 @ Bring down ending time.
    t -                  @ Subtract starting time.
    B\->R                @ Convert to real number.
    96. -                @ Subtract ticks for TICKS 't' STO
                         @ (adjust this value as needed for
                         @ results about zero from very fast EVAL;
                         @ see the notes).
    "Ticks" \->TAG       @ Tag it.
    DUP                  @ Copy Ticks value.
    8192. / '1_s' *      @ Convert to seconds.
    4. RND               @ Round to 4 decimal places.
    ROT                  @ Bring down original wordsize.
    STWS                 @ Restore starting word size.
  \>>                    @ Discard local variable.
\>>                      @ End program.


But on the 28 series, the TICKS command isn't available. You can
get around that by using a SYSEVAL sequence to get the system
time.

First off, use caution with SYSEVAL; it just executes whatever's
at the given address, without checking whether it's a valid entry
point or that any required arguments are available. For the 28C,
be sure that the user binary integer is entered with the
calculator in the correct HEX, DEC, OCT, or BIN mode to match the
value, and, on a 28S, include the correct trailing h, d, o, or b
with the integer. Visually verify that the binary integer is
actually correct. Incorrect use of SYSEVaL is all too likely to
result in a "Memory Lost".

Second, the proper address varies with the ROM version. To get the
ROM version, on a 28C, first be sure that the calculator is in HEX
mode, and then execute:

# A SYSEVAL

on a 28S, execute:

# Ah SYSEVAL

So far as I know, the possible versions are 1BB or 1CC on a 28C,
or 2BB on a 28S. The following can be used to get the system time.

28C Version 1BB:  # 123E SYSEVAL
28C Version 1CC:  # 1266 SYSEVAL
28S Version 2BB:  # 11CAh SYSEVAL

Another difference on the 28 series is that RND doesn't round to a
given number of decimal places, but rather to the current real
number display mode, so rounding involves (temporarily) changing
the display mode.

Yet another difference from the 48/49 series is that tagged
objects aren't available, so I use a character string for the
ticks value.

Unit values take two stack levels on a 28 series, so I use a
string for the seconds too.

In the following, "\<<" and "\>>" represent the program
delimiters, "\->" the right arrow (character 141 decimal), and "@"
and anything following on the same line is a comment, so shouldn't
be keyed in.

Source code for the 28 series timer program:

\<<                      @ Begin program.
  MEM                    @ Force garbage collection.
  \-> t                  @ Store MEM result in dummy local var.
  \<<                    @ start local var defining procedure.

                         @ Address for SYSEVAL.
                         @ Choose one value, depending on ROM
                         @ version (enter in HEX mode):
    # 123E               @ 28C Version 1BB
    # 1266               @ 28C Version 1CC
    # 11CAh              @ 28S Version 2BB

    SYSEVAL              @ Start time (see notes for 28 series).
    't' STO              @ Store in local variable.
    EVAL                 @ EVAL whatever's on level 1.

                         @ Address for SYSEVAL.
                         @ Choose one value, depending on ROM
                         @ version (enter in HEX mode):
    # 123E               @ 28C Version 1BB
    # 1266               @ 28C Version 1CC
    # 11CAh              @ 28S Version 2BB

    SYSEVAL              @ Ending time (see notes for 28 series).
    RCLF                 @ Original flags.
    64 STWS              @ Force 64-bit wordsize.
    SWAP                 @ Bring down ending time.
    t -                  @ Subtract starting time.
    B\->R                @ Convert to real number.
    116 -                @ Subtract ticks for # Address SYSEVAL
                         @ 't' STO (adjust this value as needed
                         @ for results about zero from very fast
                         @ EVAL; see the notes).
    DUP                  @ Make a copy.
    \->STR               @ Convert to string.
    "Ticks= "            @ Prefix for string.
    SWAP +               @ Join strings.
    SWAP                 @ Bring ticks value down.
    8192 /               @ Convert to seconds.
    4 FIX                @ Force 4-digit display mode.
    RND                  @ Round to 4 decimal places.
    \->STR               @ Convert to string.
    "Seconds= "          @ Prefix for string.
    SWAP +               @ Join strings.
    ROT                  @ Bring down original flags.
    STOF                 @ Restore original flags.
  \>>                    @ Discard local variable.
\>>                      @ End program.