[comp.sys.hp] HP28C

bmaraldo@watdcsu.UUCP (10/18/87)

  When I compare the results of 253! from the programs below, I get
  accuracy differences.  What is the reason for this?  

  Program 1 (stack manipulation):     Program 2 (local variables):

  <<				      <<
   253 DUP DUP			       1 
   WHILE 1 > REPEAT		       1 253 FOR I
     1 - DUP				I *
     3 ROLL			       NEXT
     * SWAP			      >>
     DUP
   END
   DROP
 >>

 Results:   5.17346 09926 9 E 499  	FACT function
	    5.17346 09926 4 E 499	Program 2	
	    5.17346 09928 2 E 499	Program 1	
-- 
               --------     Unit 36 Research     ---------
	                "Alien Technology Today"
               -------------------------------------------
	                    bmaraldo@watdcsu

prestonb@hpcvra.HP.COM (Preston Brown) (10/20/87)

The differences are caused by rounding error and the
order the multiplications are performed.  In one program
the values are calculated from 253 down in the other from 
one up.  The FACT function uses double precision and therefore
is the correct answer.

Preston Brown
..!hplabs!hp-pcd!prestonb

daver@hpsgpa.HP.COM (Dave Rabinowitz) (10/27/87)

>  When I compare the results of 253! from the programs below, I get
>  accuracy differences.  What is the reason for this?  

> Results:   5.17346 09926 9 E 499  	FACT function
>            5.17346 09926 4 E 499	Program 2	
>            5.17346 09928 2 E 499	Program 1	

The exact result for 253! will have 500 digits, so there will be rounding
errors whenever it is calculated with fewer than 500 digits.  The FACT 
function, and all other built-in functions, carry extra significant digits to
reduce the rounding errors in the final result, and the result is usually
accurate to the last digit.  When you run a program, each time a built-in
function is completed the result is rounded to 12 digits and this rounded
result is used in further calculations, so the result will not be as accurate
as the built-in function, which uses at least 14 digits in all its
calculations.  The reason the two programs give different answers is that they
do the multiplications in a different order, the first doing 253*252*251...*1
while the second does 1*2*3*...*253, which will lead to different rounding
errors.

jurjen@cwi.nl (Jurjen N.E. Bos) (02/23/88)

Introduction
Warning: people not having version 1BB can get disastous results.
The HP28 objects consists of a routine pointer, followed by the
data field.
The HP28C is adressed in nibbles.
Pointers are, as you will know, 5 nibbles.
The routine pointer points directly to a machinecode subroutine that
executes the code for that kind of object.
The data field of an object can be anything.


Types, as far as I know:
Pointer	Type	Followed by (size in parentheses)

02911	pointer	value(5)
02933	real	sign(1),mantissa(12),exponent+500(3)
02955	high r.	sign(1),mantissa(15),exponent+50000(5)
02977	complex	real,imag part (2*16)
0299D	high c.	real,imag part (2*21)
029BF	byte	value(2)
029E1	?
02A0A	array	size(5),type(5),#dim(5),dimensions(5)...,data
02A2C	?
02A4E	string	size(5),data(2)...
02A70	binary	size(5),data(1)...
02A96	list	data(5)...,02F90
02AB8	?
02ADA	algebr.	data(5)...,02F90
02C67	program 27F0A,data(5)...,27F1F,02F90
02C96	code	size(5),...
02D12	name	#letters(2),data(2)...
02D37	local n. #letters(2),data(2)...
02D5C	function number(2*3)
067A1	true
067E0	false


Interesting addresses: (routines use pointer arguments, and don't
check anything!)
0201E	DEPTH
0205A	DUP
0207E	DUP2
020AB	DUPN
020F5	SWAP
02116	DROP
0212A	DROP2
02140	DROPN
02067	ROT
02094	OVER
020B4	PICK
020F7	ROLL
02241	ROLLD
03495	CDR  (really!)
034DC	+ for strings
03422	convert string to byte
03599	+ for lists
O369D	+ string+byte
0372B	make structure given type and size
03845	->PROG
03859	->LIST
0386D	->ALG ebraic
0389B	STRUCT->
067FA	XOR
06812	NOT
0684E	== for reals
06866	AND
06895	OR
068B7	== for any object
06982	get type
069C4	000000h =
069E5	000000h !=
06A02	<
06A37	== for pointers
06A6C	!= for pointers
06AA1	>
06ADA	+
06AFE	-
06B0D	1+
06B2C	1-
06B4B	2+
06B6C	2-
06B8D	2*
06BAC	2/
06BCF	*
06C04	DIVMOD


06C98-06F04 are pointer constants of all kinds of values.
177F1-1BF4C are the routine for the user commands;
they check stack depth, do LAST and call an internal routine.

1C490	number->pointer
1C565	pointer->number
1C952	bool to number
1EA55	number to bool

Interesting programs:

Instead of <n> SYSEVAL one can put the pointer to n directly in the program
code, as will be shown later. I write <n>h if I mean either <n> SYSEVAL or
the repolacement.

SYS without EVAL (very nice for peeking in the ROM):

SYS
<< 06D6Ah 32F25h >>

READ
<< 10 - HEX
  4 1 FOR R
    "" 1 4 START
      OVER 06D6Ah 32F25h 04358h 1C565h R->B
      #100000 + ->STR
      DUP SIZE DUP 4 - SWAP SUB
      " " + SWAP +
      SWAP 5 + SWAP
    NEXT 1 23 SUB R DISP
  -1 STEP 10 + >>

SS
<< DEPTH ->LIST 'T' STO 4FFABh
  READ DROP T LIST-> DROP
  'T' PURGE >>

->PRG Convert contents of stack to program. You don't get << and >>,
but they're unnecessary anyway.
<< 0201Eh 03845h >>


I hope this is clear; please mail me any special requests;
I'll post the answers, if it isn't too much.

Enjoy the internals of your machine! Jurjen
No I'm NOT a saint!

jurjen@cwi.nl (Jurjen N.E. Bos) (03/04/88)

I heard my earlier message did not reach everyone -- so I repost a
clarified version.

Introduction
Warning: people not having version 1BB can get disastous results.
The HP28 objects consists of a routine pointer, followed by the
data field.
The HP28C is adressed in nibbles.
Pointers are, as you will know, 5 nibbles.
The routine pointer points directly to a machinecode subroutine that
executes the code for that kind of object.
The only objects having a printable form are the "normal" objects
that are in the manual. Other objects give "System Object", whatever
they are.
The data field of an object can be anything.


Types, as far as I know:
Pointer	Type	Followed by (size in parentheses)

02911	word	value(5)
02933	real	sign(1),mantissa(12),exponent+500(3)
02955	high r.	sign(1),mantissa(15),exponent+50000(5)
02977	complex	real,imag part (2*16)
0299D	high c.	real,imag part (2*21)
029BF	byte	value(2)
029E1	?
02A0A	array	size(5),type(5),#dim(5),dimensions(5)...,data
02A2C	?
02A4E	string	size(5),data(2)...
02A70	binary	size(5),data(1)...
02A96	list	data(5)...,02F90
02AB8	?
02ADA	algebr.	data(5)...,02F90
02C67	program 27F0A,data(5)...,27F1F,02F90
02C96	code	size(5),...
02D12	name	#letters(2),data(2)...
02D37	local n. #letters(2),data(2)...
02D5C	function number(2*3)
067A1	true
067E0	false


Interesting addresses: (routines use word arguments, and don't
check anything!)
0201E	DEPTH
0205A	DUP
0207E	DUP2
020AB	DUPN
020F5	SWAP
02116	DROP
0212A	DROP2
02140	DROPN
02067	ROT
02094	OVER
020B4	PICK
020F7	ROLL
02241	ROLLD
03495	CDR  (the LISP one)
034DC	+ for strings
03422	convert string to byte
03599	+ for lists
O369D	+ string+byte
0372B	make structure given type and size
03845	->PROG
03859	->LIST
0386D	->ALG ebraic
0389B	STRUCT->
067FA	XOR
06812	NOT
0684E	== for reals
06866	AND
06895	OR
068B7	== for any object
06982	get type
069C4	000000h =
069E5	000000h !=
06A02	<
06A37	== for words
06A6C	!= for words
06AA1	>
06ADA	+
06AFE	-
06B0D	1+
06B2C	1-
06B4B	2+
06B6C	2-
06B8D	2*
06BAC	2/
06BCF	*
06C04	DIVMOD


06C98-06F04 are word constants of all kinds of values.
177F1-1BF4C are the routine for the user commands;
they check stack depth, do LAST and call an internal routine.
examples:
+	18F9D
pi	18EB7
1A242	STO

The following routines are handy conversions.
1C490	number->word
1C565	word->number
1C952	bool to number
1EA55	number to bool


Interesting programs:

Instead of <n> SYSEVAL one can put the pointer to n directly in the program
code, as will be shown later. I write <n>h if I mean either <n> SYSEVAL or
the replacement.

SYS without EVAL (very nice for peeking in the ROM):
This routine makes a pointer to the given address. This means, for example,
that #18F9D SYS gives the object +, while #18F9D SYSEVAL adds two numbers.

SYS
<< 06D6Ah 32F25h >>

READ     read contents of ROM at given address.
<< 10 - HEX
  4 1 FOR R
    "" 1 4 START
      OVER 06D6Ah 32F25h 04358h 1C565h R->B
      #100000 + ->STR
      DUP SIZE DUP 4 - SWAP SUB
      " " + SWAP +
      SWAP 5 + SWAP
    NEXT 1 23 SUB R DISP
  -1 STEP 10 + >>

SS
<< DEPTH ->LIST 'T' STO 4FFABh
  READ DROP T LIST-> DROP
  'T' PURGE >>

  READ displays the contents of memory addresses *backwards*:
  this is very handy because addresses are stored backwards, and thus
  directly readable. Furthermore, READ increments the address with the amount
  it displays, so that it is easy to continue.

->PRG Convert contents of stack to program. You don't get << and >>,
but they're unnecessary anyway (except for debugging, don't use HALT!).
<< 0201Eh 03845h >>

Usage of ->PRG:
1) put #27F0A SYS on stack, if you want a << before your program.
2) put all object for the program on stack, in order
   (a pointer can be put on stack with #value SYS)
4) put #27F1F SYS on stack, if you want a >> after the program.
5) execute ->PRG, an there's your program.

A->P     convert algebraic to program that does the same.
<< #27F0A SYS SWAP
   32F11h
   #27F1F SYS
   ->PRG
>>


A handy way of getting an address of a known object is putting it in a brand new
variable and looking at address #4FFB0: this gives the top 80 nibbles of 
RAM. You'll soon find out how variables are stored and see your object.
This is the most reliable way for observing something.

Example: {+} 1 GET gives a single +. Putting this in the variable TP gives
as result of #4FFB0 READ:
03A00 202D5 C0250 54020
008B0 2F902 7F1F1 A2B62
7F4E2 79BC2 7F341 783F1
7B0B2 79BC1 783F4 44145

This means: (still backwards)
03A 002    02D5C 02 50 54 02
3A   2  function  2  P  T  2
Otherwise said: function number 3A,2 stored in variable PT. The name
PT has two letters.

More informative is << + >> 'TP' STO #$FFB0 READ:
02F90 27F1F 18F9D 27F0A
end   >>    +     <<
02C67    02 50 54 02
program  2  P  T  2

This learns us that the function + is to be found in ROM at 18F9D.
You'll find there the code that does +, written normal way:
02C67 program
1C3A5 check two levels stack & save LAST registers
1C6FA CASE statement on type of top levels
06E00 word 00011: this means two reals
1EC68 + for two reals
06E0A word 00012: real and complex
3323C + for real and complex
...
02F90 end

The amount of FORTH code is almost endless: you don't have to know
the machine code to come rather far (but it helps).

I hope this is clear; please mail me any special requests;
I'll post the answers, if it isn't too much.

Enjoy the internals of your machine! -- Jurjen
No I'm NOT a saint!