Category Archives: articles

HP-25 – YTOX

The Problem

I had someone ask me recently about “ytox” (y^x) on a HP-25. He’d done 2 ENTER 29 f y^x, got 536870908.6 back, and thought that was wrong.

It is hardly surprising that he thought it was wrong – very few powers of two end up with “.6” on the end. 29 shouldn’t be one of those.

Perhaps it was a precision thing. We’re clearly pushing the bounds of what is reasonable here.

Other Calculators

However, he then said “it works fine on a HP-67”. That blew the whole 14 BCD digits with only 10 digits in the mantissa (significant digits) idea out of the park. Both calculators treat numbers the same way internally and both the HP-25 and HP-67 even have the same processor in them.

I tried it. He was right. In both cases – it was wrong on the HP-25 and it was right on the HP-67.

Out of curiosity, I tried it on a HP-29 and got the correct result. I, and probably many others, consider the HP-29 to be a HP-25 with upgrades. It’s in the same case. It has, pretty much, the same keys – and they’re in the same spots too. Sure, the HP-29 is a much nicer calculator. They added quite a lot to the HP-25 to get there. But the starting point was obviously a HP-25. With them focused on adding things, I did not expect they had much time for rewriting what was already done. It was a surprise to see that y^x had been improved.

Looking at the dates for the calculators using the unofficial hpmuseum.org site shows the sequence as: 1975 HP-25, 1976 HP-25C, 1976 HP-67, 1977 HP-29C. Before the HP-25 was: 1974 the HP-65 (and early in 1975, the HP-55).

That also suggested, “what about the HP-65?” There’s one of those lying around (HP-65). Unlike its upgrade, the HP-67, it gives the same answer as the HP-25. It got it wrong (slightly). The correct answer is 536870912.0 not 536870908.6.

Whilst I do have access to a HP-55 (not yet published on this site as, whilst interesting, it wasn’t one I’d used); I didn’t really check what it gave. If the HP-65 before it gave the wrong answer, and the HP-25 after it also gave the wrong answer, it was likely to give the same wrong answer. I’ve just checked and it does.

The early HP calculators get this one wrong (slightly). The later ones don’t. The crossover point seems to be the HP-25.

Late in 1975 or early in 1976 HP improved the microcode in their newer calculators to get this right.

Why

So then, why do the earlier ones get it wrong?

This will get a bit technical. I’m skipping over a lot and summarising big chunks in order to make it a bit easier to follow. However, if that’s not your thing, feel free to skip over this.

HP-25 - ytox (y^x)

2 ENTER 29 f y^x

1. 00742 (WaitLoop) [2] pressed. Get:
; M2=02000000000000 A= 02ffffffffffff B= 21000000000000 " 2.          "

2. 00742 (WaitLoop) [ENTER] pressed. Get:
; D= 02000000000000
; M2=02000000000000 A= 0200ffffffffff B= 21000000000000 " 2.00        "
s2 and s8 have been cleared. s8 is InputMode. s2 is AutoEnter.

3. 00742 (WaitLoop) [2] pressed. Get:
; D= 02000000000000
; M2=02000000000000 A= 02ffffffffffff B= 21000000000000 " 2.          "
s2 and s8 are set again

4. 00742 (WaitLoop) [9] pressed. Get:
; D= 02000000000000
; M2=02900000000001 A= 029fffffffffff B= 20100000000000 " 29.         "
s2 and s8 are still set. s13 is also set.
y value is in D. x value (2.9x10^1) is in M2

5. 00742 (WaitLoop) [f] pressed. Get:
; D= 02000000000000
; M2=02900000000001 A= 029fffffffffff B= 20100000000000 " 29.         "
main effect was to set s9 (f_Pressed)
InputMode has ended (s8 cleared). s2 is still set. s13 was cleared also.

6. 00742 (WaitLoop) [3] pressed ([f] [3] = y^x). Get:

6a. Determine which function to run and save Lastx on the way

00762 display off
...
00770 keys -> rom address
...
00647 c + 1 -> c[x]                      ; C= 00000000000003
00650 return
...
00675 load constant 12                   ; C= 000000000000c3 P= 0
; HexCode c3 is [3]
...
00701 if 0 = s 9 then goto 00616         ; if not f_Pressed (no, it is)
00703 c - 1 -> c[p]                      ; C= 000000000000b3
00704 c - 1 -> c[p]                      ; C= 000000000000a3
; HexCode a3 is y^x ([f] [3])
...
01770 a + 1 -> a[xs]                     ; A= 201000000001a3
01771 a -> rom address // 1a = 0001 1010 = 00 011 010 = 032 octal
; A[2,1]=0x1a. Octal=032 In ROM 01400 so -> 01432
...
02434 shift left a[x]                    ; A= 20100000000a30
02435 a exchange c[xs]                   ; A= 20100000000130 ...
; get x value and save in register 8 as Lastx
02436 m2 -> c                            ; C= 02900000000001
02437 c -> data register 8               ; save Lastx

02440 0 -> b[w]                          ; B= 00000000000000
02441 p <- 12                            ; P= 12
02442 clear s                            ; S= ..23.5.........f
02443 decimal
02444 a -> rom address
; A[2,1]=0x13. Octal=023. In ROM 02400 so -> 02423

; (now running HexCode 0xa3)
02423 if n/c goto 02771

6b. Calculate LN y

do_ytox:
02771 stack -> a                         ; A= 02000000000000 (y value, 2)
...
 03321 if a[s] # 0 then goto 01300
 03323 a exchange c[m]                   ; A= 09900000000000 C= 06931471805999
 03324 c -> a[w]                         ; A= 06931471805999 <---- this is LN(2)
 03325 0 -> b[w]
 03326 p <- 12
 03327 return
; 999 at end of C and A is EEX -1 so 6.931471805*10^-1 = 0.6931471805

6c. Multiply by x 

03332 m2 -> c                            ; C= 02900000000001 <--- this is x for y^x
03333 if n/c goto 03114
03114 jsb 03334 // MULTIPLY A=C*A; C=A
 ; In : A= 06931471805999 (0.69... ie 6.9...e-01) C=02900000000001
 ; Out: A= 02010126823001 C= 02010126823001 (=2.010126823e+01 =20.10126823)
 03334 ...
 03323 a exchange c[m]                   ; A= 09999999999900 C= 02010126823001
 03324 c -> a[w]                         ; A= 02010126823001 <-- 29*LN(2)
 03325 0 -> b[w]                         ; B= 00000000000000
 03326 p <- 12
 03327 return

6d. Calculate e ^ (LN(2)*29) // note e ^ (LN(y) * x) = y^x

03115 jsb 03162 ; return C= 02302585093000 P= 12
03116 jsb 03101
 ; In:  A= 02010126823001 (LN(2)*29) C= 02302585093000
 ; Out: A= 00168058748600 B= 00000000000008 P= 11 C= 00500000000000
 03274 return

03117 jsb 03360 ; return if (P==11) { C=00693147180553; P=12 } 
03120 p <- 11                            ; P= 11
03121 jsb 03066
 ; In:  A= 00168058748600 B= 00000000000008 C= 00693147180553 P= 11
 ; Out: A= 00294293124894 B= 20000000000008 C= 00950000000000
 03274 return

03122 jsb 03144 ; return C=[0095]3101798055 P= 12 // keeps 1st 4 digits
03123 p <- 10                            ; P= 10
03124 jsb 03066
 ; In:  A= 00294293124894 B= 20000000000008 C= 00953101798055 P= 10
 ; Out: A= 00083625854775 B= 32000000000008 C= 00995000000000 P= 9
 03274 return

03125 jsb 03200 ; return C=[009950]33085093 P= 12 // keeps 1st 6 digits
03126 p <- 9                             ; P= 9
03127 jsb 03066
 ; In:  A= 00083625854775 B= 32000000000008 C= 00995033085093 P= 9
 ; Out: A= 00836258547750 B= 03200000000008 C= 00999500000000 P= 8
 03274 return

03130 jsb 03330 ; return C=[00999500]330850 P= 12 // keeps 1st 8 digits
03131 p <- 8                             ; P= 8
03132 jsb 03066
 ; In:  A= 00836258547750 B= 03200000000008 C= 00999500330850 P= 8
 ; Out: A= 00366582830700 B= 80320000000008 C= 00999950000000 P= 7
 03274 return

03133 jsb 03066
 ; In:  A= 00366582830700 B= 80320000000008 C= 00999950000000 P= 7
 ; Out: A= 00665978307000 B= 38032000000008 C= 00999995000000 P= 6
 03274 return

03134 jsb 03066
 ; In:  A= 00665978307000 B= 38032000000008 C= 00999995000000 P= 6
 ; Out: A= 00659813070000 B= 63803200000008 C= 00999999500000 P= 5
 03274 return

03135 p <- 6                             ; P= 6
03136 0 -> a[wp]                         ; A= 00659810000000
03137 p <- 13                            ; P= 13
03140 b exchange c[w]                    ; B= 00999999500000 C= 63803200000008
03141 a exchange c[w]                    ; A= 63803200000008 C= 00659810000000
03142 load constant 6                    ; C= 60659810000000 P= 12
03143 if n/c goto 03224

03224 a exchange c[w]                    ; A= 60659810000000 C= 63803200000008
03225 a - 1 -> a[s]                      ; A= 50659810000000
03226 if n/c goto 03216
03216 a -> b[w]                          ; B= 50659810000000
03217 c - 1 -> c[s]                      ; C= 53803200000008
03220 if n/c goto 03214

03214 jsb 03373 ; // a= b + a[wp]/(10^a[s])
 ; In:  A= 50659810000000 B= 50659810000000 (same) A[s]=5 P= 12
 ; Out: A= 50659816598100 B= 50659810000000 A[s]=5 P=12, B A[s] P used; not changed
 03373 while (a[s]--, !CY)
 03372   shift right a[wp]
         ; A[s]=4 A[wp]=0065981000000
         ; A[s]=3 A[wp]=0006598100000
         ; A[s]=2 A[wp]=0000659810000
         ; A[s]=1 A[wp]=0000065981000
         ; A[s]=0 A[wp]=0000006598100
         ; A[s]=9 CY
 03375 0 -> a[s]                         ; A= 00000006598100
 03376 a + b -> a[w]                     ; A= 50659816598100
 03377 return

03215 a + 1 -> a[p]                      ; A= 51659816598100 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 51659816598100 // b= a
03217 c - 1 -> c[s]                      ; C= 43803200000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=5 P=12 A[wp]=1659816598100 (out= 1.00001*in [wp])
 03376 a + b -> a[w]                     ; A= 51659833196265
 03377 return

03215 a + 1 -> a[p]                      ; A= 52659833196265 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 52659833196265 // b= a
03217 c - 1 -> c[s]                      ; C= 33803200000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=5 P=12 A[wp]=2659833196265
 03376 a + b -> a[w]                     ; A= 52659859794596
 03377 return

03215 a + 1 -> a[p]                      ; A= 53659859794596 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 53659859794596 // b= a
03217 c - 1 -> c[s]                      ; C= 23803200000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=5 P=12 A[wp]=3659859794596
 03376 a + b -> a[w]                     ; A= 53659896393193
 03377 return

03215 a + 1 -> a[p]                      ; A= 54659896393193 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 54659896393193 // b= a
03217 c - 1 -> c[s]                      ; C= 13803200000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=5 P=12 A[wp]=4659896393193
 03376 a + b -> a[w]                     ; A= 54659942992156
 03377 return

03215 a + 1 -> a[p]                      ; A= 55659942992156 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 55659942992156 // b= a
03217 c - 1 -> c[s]                      ; C= 03803200000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=5 P=12 A[wp]= 5659942992156
 03376 a + b -> a[w]                     ; A= 55659999591585
 03377 return

03215 a + 1 -> a[p]                      ; A= 56659999591585 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 56659999591585 // b= a
03217 c - 1 -> c[s]                      ; C= 93803200000008 CY // loop counter-- EXIT
03220 if n/c goto 03214

; divide by 10
03221 shift right a[wp]                  ; A= 50665999959158
; shift next digit to loop counter in c[s]
03222 a exchange c[w]                    ; A= 93803200000008 C= 50665999959158
03223 shift left a[ms]                   ; A= 38032000000008
03224 a exchange c[w]                    ; A= 50665999959158 C= 38032000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s]                      ; A= 40665999959158
03226 if n/c goto 03216

03216 a -> b[w]                          ; B= 40665999959158 // b= a
03217 c - 1 -> c[s]                      ; C= 28032000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=4 P=12 A[wp]= 0665999959158
 03376 a + b -> a[w]                     ; A= 40666066559153
 03377 return

03215 a + 1 -> a[p]                      ; A= 41666066559153 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 41666066559153 // b= a
03217 c - 1 -> c[s]                      ; C= 18032000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=4 P=12 A[wp]= 1666066559153
 03376 a + b -> a[w]                     ; A= 41666233165808
 03377 return

03215 a + 1 -> a[p]                      ; A= 42666233165808 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 42666233165808 // b= a
03217 c - 1 -> c[s]                      ; C= 08032000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=4 P=12 A[wp]= 2666233165808
 03376 a + b -> a[w]                     ; A= 42666499789124
 03377 return

03215 a + 1 -> a[p]                      ; A= 43666499789124 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 43666499789124 // b= a
03217 c - 1 -> c[s]                      ; C= 98032000000008 CY // loop counter-- EXIT
03220 if n/c goto 03214

; divide by 10
03221 shift right a[wp]                  ; A= 40366649978912
; shift next digit to loop counter in c[s]
03222 a exchange c[w]                    ; A= 98032000000008 C= 40366649978912
03223 shift left a[ms]                   ; A= 80320000000008
03224 a exchange c[w]                    ; A= 40366649978912 C= 80320000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s]                      ; A= 30366649978912
03226 if n/c goto 03216

03216 a -> b[w]                          ; B= 30366649978912 // b=a
03217 c - 1 -> c[s]                      ; C= 70320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 0366649978912
 03376 a + b -> a[w]                     ; A= 30367016628890
 03377 return

03215 a + 1 -> a[p]                      ; A= 31367016628890 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 31367016628890 // b= a
03217 c - 1 -> c[s]                      ; C= 60320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 1367016628890
 03376 a + b -> a[w]                     ; A= 31368383645518
 03377 return

03215 a + 1 -> a[p]                      ; A= 32368383645518 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 32368383645518 // b=a
03217 c - 1 -> c[s]                      ; C= 50320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 2368383645518
 03376 a + b -> a[w]                     ; A= 32370752029163
 03377 return

03215 a + 1 -> a[p]                      ; A= 33370752029163 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 33370752029163 // b=a
03217 c - 1 -> c[s]                      ; C= 40320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 3370752029163 
 03376 a + b -> a[w]                     ; A= 33374122781192
 03377 return

03215 a + 1 -> a[p]                      ; A= 34374122781192 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 34374122781192 // b=a
03217 c - 1 -> c[s]                      ; C= 30320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 4374122781192
 03376 a + b -> a[w]                     ; A= 34378496903973
 03377 return

03215 a + 1 -> a[p]                      ; A= 35378496903973 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 35378496903973 // b=a
03217 c - 1 -> c[s]                      ; C= 20320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 5378496903973
 03376 a + b -> a[w]                     ; A= 35383875400876
 03377 return

03215 a + 1 -> a[p]                      ; A= 36383875400876 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 36383875400876 // b=a
03217 c - 1 -> c[s]                      ; C= 10320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 6383875400876
 03376 a + b -> a[w]                     ; A= 36390259276276
 03377 return

03215 a + 1 -> a[p]                      ; A= 37390259276276 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 37390259276276 // b=a
03217 c - 1 -> c[s]                      ; C= 00320000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=3 P=12 A[wp]= 7390259276276
 03376 a + b -> a[w]                     ; A= 37397649535552
 03377 return

03215 a + 1 -> a[p]                      ; A= 38397649535552 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 38397649535552 // b=a
03217 c - 1 -> c[s]                      ; C= 90320000000008 CY // loop counter--
03220 if n/c goto 03214

; divide by 10
03221 shift right a[wp]                  ; A= 30839764953555
; shift next digit to loop counter in c[s]
03222 a exchange c[w]                    ; A= 90320000000008 C= 30839764953555
03223 shift left a[ms]                   ; A= 03200000000008
03224 a exchange c[w]                    ; A= 30839764953555 C= 03200000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s]                      ; A= 20839764953555
03226 if n/c goto 03216

03216 a -> b[w]                          ; B= 20839764953555 // b=a
03217 c - 1 -> c[s]                      ; C= 93200000000008 CY // loop counter--
03220 if n/c goto 03214

; divide by 10
03221 shift right a[wp]                  ; A= 20083976495355
; shift next digit to loop counter in c[s]
03222 a exchange c[w]                    ; A= 93200000000008 C= 20083976495355
03223 shift left a[ms]                   ; A= 32000000000008
03224 a exchange c[w]                    ; A= 20083976495355 C= 32000000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s]                      ; A= 10083976495355
03226 if n/c goto 03216

03216 a -> b[w]                          ; B= 10083976495355 // b=a
03217 c - 1 -> c[s]                      ; C= 22000000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=1 P=12 A[wp]= 0083976495355
 03376 a + b -> a[w]                     ; A= 10092374144890
 03377 return

03215 a + 1 -> a[p]                      ; A= 11092374144890 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 11092374144890 // b=a
03217 c - 1 -> c[s]                      ; C= 12000000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=1 P=12 A[wp]= 1092374144890
 03376 a + b -> a[w]                     ; A= 11201611559379
 03377 return

03215 a + 1 -> a[p]                      ; A= 12201611559379 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 12201611559379 // b=a
03217 c - 1 -> c[s]                      ; C= 02000000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=1 P=12 A[wp]= 2201611559379
 03376 a + b -> a[w]                     ; A= 12421772715316
 03377 return

03215 a + 1 -> a[p]                      ; A= 13421772715316 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 13421772715316 // b=a
03217 c - 1 -> c[s]                      ; C= 92000000000008 CY // loop counter--
03220 if n/c goto 03214

; divide by 10
03221 shift right a[wp]                  ; A= 10342177271531
; shift next digit to loop counter in c[s]
03222 a exchange c[w]                    ; A= 92000000000008 C= 10342177271531
03223 shift left a[ms]                   ; A= 20000000000008
03224 a exchange c[w]                    ; A= 10342177271531 C= 20000000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s]                      ; A= 00342177271531
03226 if n/c goto 03216

03216 a -> b[w]                          ; B= 00342177271531 // b=a
03217 c - 1 -> c[s]                      ; C= 10000000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=0 P=12 A[wp]= 0342177271531
 03376 a + b -> a[w]                     ; A= 00684354543062
 03377 return

03215 a + 1 -> a[p]                      ; A= 01684354543062 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 01684354543062 // b=a
03217 c - 1 -> c[s]                      ; C= 00000000000008 // loop counter--
03220 if n/c goto 03214

03214 ... ; A[s]=0 P=12 A[wp]= 1684354543062
 03376 a + b -> a[w]                     ; A= 03368709086124
 03377 return

03215 a + 1 -> a[p]                      ; A= 04368709086124 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w]                          ; B= 04368709086124 // b=a
03217 c - 1 -> c[s]                      ; C= 90000000000008 CY // loop counter--
03220 if n/c goto 03214

; divide by 10
03221 shift right a[wp]                  ; A= 00436870908612
; shift next digit to loop counter in c[s]
03222 a exchange c[w]                    ; A= 90000000000008 C= 00436870908612
03223 shift left a[ms]                   ; A= 00000000000008
03224 a exchange c[w]                    ; A= 00436870908612 C= 00000000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s]                      ; A= 90436870908612 CY
03226 if n/c goto 03216
; exit loop - for (a[s]=5; a[s]>=0; a[s]--) // real a[s] is limited 0..9(or f) so break if a[s]--, CY

03227 a exchange b[w]                    ; A= 04368709086124 B= 90436870908612
03230 a + 1 -> a[p]                      ; A= 05368709086124
03231 jsb 03305
 ; sign to +ve, round to 10 digits, add in exponent
 ; In:  A=value (mantissa to 13 digits) C[2,1,0]=exponent. Uses B
 ; Out: A=value (rounded, mantissa and exponent). C=A B=0 P=12
 03305 0 -> a[s]
 03306 p <- 12
 03307 0 -> b[w]                         ; B= 00000000000000
 03310 if a[p] # 0 then goto 01317       ; if mantissa starts with zero, shift left and adj exponent
 ; add last few digits (rounds if these are "500" or more)
 03317 a -> b[x]                         ; B= 00000000000124
 03320 a + b -> a[w]                     ; A= 05368709086248 
 03321 if a[s] # 0 then goto 01300       ; if overflowed into sign
 03323 a exchange c[m]                   ; A= 00000000000248 C= 05368709086008
 03324 c -> a[w]                         ; A= 05368709086008
 03325 0 -> b[w]                         ; B= 00000000000000
 03326 p <- 12
 03327 return

; At this point A= C= 05368709086008
; This is e^(LN(2)*29) = ytox(2,29) = answer = +5.368709086e+08 = 536870908.6
; 2^29 should be 536870912.0

6e. Display

03232 select rom 4
02233 if n/c goto 02340
02340 m2 exch c                          ; C= 02900000000001 M2=05368709086008
; 029...01 = what was in X previously (29)
; 053...08 = HP-25 answer to 2 ENTER 29 y^x
02341 1 -> s 2                           ; AutoEnter
02342 select rom 3
01743 clear s                            ; S= ..23.5.........f
01744 delayed rom 0
01745 jsb 01705
 // get X value, check for 0, prob adjust if too small or too big (not shown here)
 00305 m2 -> c                           ; C= 05368709086008
 00306 decimal
 00307 p <- 0                            ; P= 0
 00310 if c[m] = 0 then goto 00272       ; no - mantissa isn't zero
 00312 if c[xs] = 0 then goto 00321      ; yes - exponent is 0 or +ve
 00321 return

01746 m2 exch c
01747 if p # 12 then goto 01752
01752 if 0 = s 1 then goto 00247
00247 display off
00250 decimal
00251 0 -> b[w]
00252 0 -> a[w]                          ; A= 00000000000000
00253 f exch a                           ; A= 00000000000001; // _f <-> A[0]
00254 jsb 00341
 ; create display mask
 00341 0 -> a[w]                         ; A= 00000000000000
 00342 a + 1 -> a[s]                     ; A= 10000000000000
 00343 shift right a[w]                  ; A= 01000000000000
 00344 a + 1 -> a[s]                     ; A= 11000000000000
 00345 a + 1 -> a[s]                     ; A= 21000000000000
 00346 a exchange b[ms]                  ; A= 00000000000000 B= 21000000000000
 00347 f -> a ; // A[0]=_f
 00350 p <- 0
 00351 a - 1 -> a[p]                     ; A= 00000000000009 CY
 00352 if n/c goto 00233
 00353 a exchange b[x]                   ; A= 00000000000000 B= 21000000000009
 00354 0 -> b[x]                         ; B= 21000000000000
 00355 return

00255 m1 -> c                            ; C= 20000000000202
00256 c -> a[x]                          ; A= 00000000000202
00257 0 -> a[xs]                         ; A= 00000000000002 // 02=2 decimal places
00260 delayed rom 2
00261 if n/c goto 00203
01203 0 -> s 14
01204 c - 1 -> c[xs]                     ; C= 20000000000102
01205 if n/c goto 01212
01212 c - 1 -> c[xs]                     ; C= 20000000000002
01213 if n/c goto 01305                  ; n/c so FIX mode
01305 m2 -> c                            ; C= 05368709086008
01306 a + c -> a[x]                      ; A= 00000000000010
01307 1 -> s 13                          ; S= ..23.5.......d.f
01310 jsb 01361
 // check if fits in FIX mode
 // will end with P=2 or P>2
 // if P>2 some digits on RHS will be blanked later (eg " 1.00  " not " 1.00000...")
 01361 p <- 12                           ; P= 12
 01362 a + 1 -> a[x]                     ; A= 00000000000011
 01363 if n/c goto 01356
 01356 a - 1 -> a[x]                     ; A= 00000000000010
 01357 if n/c goto 01353
 01353 if p = 2 then goto 01364
 01355 p - 1 -> p                        ; P= 11
 01356 a - 1 -> a[x]                     ; A= 00000000000009
 01357 if n/c goto 01353
 01353 ...                               ; P= 10 A= 00000000000008
 01353 ...                               ; P= 9  A= 00000000000007
 01353 ...                               ; P= 8  A= 00000000000006
 01353 ...                               ; P= 7  A= 00000000000005
 01353 ...                               ; P= 6  A= 00000000000004
 01353 ...                               ; P= 5  A= 00000000000003
 01353 ...                               ; P= 4  A= 00000000000002
 01355 ...                               ; P= 3  A= 00000000000001
 01353 ...                               ; P= 2  A= 00000000000000
 01353 if p = 2 then goto 01364
 01364 0 -> a[w]
 01365 c -> a[wp]                        ; A= 00000000000008
 01366 a + c -> a[m]                     ; A= 05368709086008
 01367 if n/c goto 01376
 01376 a -> b[x]                         ; B= 21000000000008
 01377 return

01311 if a[xs] # 0 then goto 01327
01313 jsb 01300  // set a[wp] to ff..f
 01300 binary
 01301 0 -> a[wp]                        ; A= 05368709086000
 01302 a - 1 -> a[wp]                    ; A= 05368709086fff CY
 01303 decimal
 01304 return

01314 a exchange b[x]                    ; A= 05368709086008 B= 21000000000fff
01315 p <- 1                             ; P= 1
01316 if a[p] # 0 then goto 01345
01320 a - 1 -> a[x]                      ; A= 05368709086007
01321 if n/c goto 01325
01325 shift right b[m]                   ; B= 20100000000fff
01326 if n/c goto 01320
01320 ...                                ; A= 05368709086006 B= 20010000000fff
01320 ...                                ; A= 05368709086005 B= 20001000000fff
01320 ...                                ; A= 05368709086004 B= 20000100000fff
01320 ...                                ; A= 05368709086003 B= 20000010000fff
01320 ...                                ; A= 05368709086002 B= 20000001000fff
01320 ...                                ; A= 05368709086001 B= 20000000100fff
01320 ...                                ; A= 05368709086000 B= 20000000010fff
01320 a - 1 -> a[x]                      ; A= 05368709086999 CY
01321 if n/c goto 01325
01322 0 -> a[x]                          ; A= 05368709086000
01323 a exchange b[x]                    ; A= 05368709086fff B= 20000000010000
01324 if n/c goto 01251
01251 c -> a[s]
01252 clear s                            ; S= ..23.5.........f
01253 0 -> s 5
01254 if 1 = s 5 then goto 00274
00274 if 0 = s 1 then goto 00072
00072 if 0 = s 3 then goto 01672
00074 delayed rom 1
00075 if n/c goto 00165
00565 0 -> s 9
00566 0 -> s 10
00567 if n/c goto 00672
00672 0 -> s 14
00673 jsb 00751
 00751 display toggle
 ;//
 ;// display " 536870908.6"
 ;//
 00752 jsb 00470
  ; debounce (wait for key up)
  00470 0 -> s 15                        ; S= ..23.5..........
  00471 p <- 7                           ; P= 7
  00472 p - 1 -> p                       ; P= 6
  00473 if p # 0 then goto 00472
  00472 ...                              ; P= 5, 4, 3, 2, 1
  00472 p - 1 -> p                       ; P= 0
  00473 if p # 0 then goto 00472
  00475 if 1 = s 15 then goto 00470
  00477 return

 00753 cpu woodstock                     // about to enter main keypress loop 
 00754 if 1 = s 3 then goto 00742

WaitLoop:
 00742 0 -> s 3                          // main keypress loop
 00743 if 0 = s 3 then goto 01672
 00745 if 0 = s 15 then goto 00742

WaitLoop:
 00742 ...
 00742 ...

It basically does: LN(2), multiplies that by 29 and then does e^x of that.

There are a few obvious solutions to making that better in later calculators: make sure LN(2) is more accurate, keep the precision of intermediate values as high as possible, make e^x more accurate, or maybe do the same process but with LOG10(2) and 10^x given that BCD (Binary Coded Decimal) is inherently base 10 anyway.

Internally, as you’ll see above, most of the working uses 13 decimal places despite the mantissa only holding 10 digits. The 3 digits on the RHS for the exponent sign and value are pressed into service to improve precision. It’s quite impressive.

As a first cut on comparisons, if I do LN(2) on a HP-25 I get “0.6931471805”. On a HP-67 I get “0.6931471806”. That might be the only reason for it being correct on later calculators and slightly low on earlier ones.

I could do a more detailed comparison, and that would certainly be interesting, but the raw listing I started with for the HP-25 y^x was just over 3600 lines of execution. Simplifying that to just over 600 lines (above) took quite a bit of work. I have some reluctance to go through that again for the HP-67 version.

Given the processors in the HP-25 and HP-67 are the same, a direct comparison of the microcode in both calculators will probably be an easier way forward. I have the addresses that apply for the HP-25. Given the way ROMs were reused in later calculators, or reused with updates, there’s a good chance the two microcode programs will match up except for a few small (but significant) updates. I’ll add another article if I get around to that comparison.

If I do Math.Log(2) on a modern computer, it gives a double precision result of 0.693147180559945. So “…805” was close, but “…806” is the correct answer to 10 digits.

When you think of the precision that was available at the time, through the careful use of a sliderule or the use of printed four figure math tables, the results from even the earlier calculators are stunning.

HP-25 Words

It is surprisingly hard to find anything on the HP-25 that doesn’t “normalize” the X value when it displays it. Normalizing is what causes 5.a00e+01 to get displayed as ” 60.00″ instead of being displayed as something more interesting.

I had thought that, perhaps, PAUSE would do it. After all, PAUSE should be pretty simple. Why would that bother to convert what it is displaying during the PAUSE. But no, not even that worked. However, despite seemingly insurmountable odds, one thing did end up working. Continue reading HP-25 Words

HP-25 Undocumented Features

The HP-25 was a great calculator. It had heaps of features right there on the keyboard. These included conversions between Rectangular and Polar coordinates and between Degrees / Minutes / Seconds and decimal degrees. It even had ENGineering notation and GRaDians. It was programmable and was, in some ways, better than the previous top of the line – the HP-65. From a programmable perspective, it had less steps (49) than the ’65 (100) but all 49 were fully merged. That meant if you needed two or three keystrokes for a function, all two or three would be stored in a single program step. The ’65 was similar; but not all combinations were fully merged. The biggest difference for me was the program display. The ’65 would usually display the keycode of the current program step, but not always. The HP-25 always displays the step number and the keycode. It is much easier to use.

What is truly impressive is the leap from the Classics to the Woodstock series of calculators. The HP-25 had SST (single step) and BST (back step), eight conditional tests, PAUSE, NOP, “hold down the key to see the program keycode” and INT and FRAC functions. When you added Continuous Memory with the HP-25C, it was even easier to load your favourite program than a HP-65 (instead of inserting a mag card, it was just “already there”). The HP-65 still excelled in some areas but the HP-25 wasn’t far from it. It’s no wonder it was so popular. I sure loved mine at the time. But all that isn’t the point of this article.

There are some things they didn’t tell you about. Continue reading HP-25 Undocumented Features

The HP-25 Run Loop

When you run a program in a programmable calculator, it goes through a standard process of:

  • get a program step to run,
  • update the program counter (PC) to point to the next step, and
  • run the step we got.

It does that over and over again, mostly regardless of what the program step is. (The exception is a [R/S] program step, which stops the process).

Here’s what it looks like from the calculator’s side of the case: Continue reading The HP-25 Run Loop

cpmfs 2.00

cpmfs tool to copy files to cpm disk images

Version 2.00 of cpmfs has just been released, and it’s pretty good even if I do say so myself.

Cpmfs is a tool that allows you to copy files to and from CP/M disk images. It runs on Windows (but should easily port to linux) so you can set up disks for an old Operating System from the comfort of your current one. I use it in automated builds but it’s designed to be easy to use manually too. Here’s what you can do: Continue reading cpmfs 2.00

INIT.COM for Cromemco CP/M

Formatting a disk in CP/M is hardware dependant. This is a bit strange because the intent of CP/M was to move the hardware dependencies to a common Basic Input Output System or BIOS, so you could run any program on any CP/M system.

They either forgot about formatting disks, or didn’t consider it happened often enough to warrant inclusion. The result is every system needs its own disk formatting program and Cromemco called theirs INIT.COM.

INIT.COM was originally fairly simple. It used to ask if you wanted to format a “Mini disk” (5″ disk) or a normal (“Maxi” / 8″) one. That program worked well and it ran under Cromemco’s CDOS operating system or under normal CP/M.

As disk formats evolved to allow double-sided disks and double-density (more data in the same amount of space), things got more complicated. Cromemco included features in INIT.COM that made it easier for a user to get the correct format; but these required things that CP/M doesn’t provide. Their solution was to include them in CDOS and, as a result, later versions of Cromemco’s INIT.COM won’t run under CP/M any more.

So how did I end up with the picture above, which shows an advanced INIT.COM running under CP/M? Continue reading INIT.COM for Cromemco CP/M

CDOS Ver 0.20 on 8 inch disks

CDOS Ver 0.20 is available on the internet for systems with small (5″) floppy disks. However, 5″ systems are painfully small. STAT.COM will tell you a 5″ disk holds 83KB but that includes 2KB of directory entries, I have always thought of them as 81KB disks.

There’s not a lot you can fit in 81KB so I’d standardized on 8″ single-sided, single-density disks for everything – other than CDOS Ver 0.20. 8SSSD was the universally agreed format for interchangeability between CP/M systems at the time so it’s a good one to pick for emulators of computers of that time.

CDOS came on 8″ disks or 5″ disks. Later versions included CDOSGEN.COM which allowed you to reconfigure the operating system to work with different amounts of installed memory and different drive configurations. My guess is, Ver 0.20 preceded CDOSGEN. There doesn’t seem to be a way to reconfigure what you have for different systems. However, … Continue reading CDOS Ver 0.20 on 8 inch disks