Finally, here is the complete map of the HP35 ROM 2 (256 instructions of 10 bits each).
ROM 2 contains the
code for:
- Cordic routines for log (ln), ex and
xy,
- prescaling routines,
- mutiplication and division,
- and result normalization.
02000: | err21: | select rom 0 | goto blinking display (0001) | |
02001: | ln24: | a exchange b[s] | Computation of a new constant (2, 1.1, 1.01, 1.001 etc) | |
02002: | a + 1 -> a[s] | # SR ++ | ||
02003: | shift right c[ms] | Make room for the next qj | ||
02004: | shift left a[wp] | align left | ||
02005: | go to ln26 | |||
02006: | xty22: | stack -> a | ax is evaluated as ln(a) . x (see at 02074) | |
02007: | jsb mpy21 | |||
02010: | c -> a[w] | Main entry point for ex and log | ||
02011: | if s8 = 0 | If S8 = 0 -> ex | ||
02012: | then go to exp21 | If S8 = 1 -> log (ln) | ||
02013: | 0 -> a[w] | |||
02014: | a - c -> a[m] | Complement X for ln(X) | ||
02015: | if no carry go to err21 | Goto error if ln(negative) | ||
02016: | shift right a[w] | |||
02017: | c - 1 -> c[s] | |||
02020: | if no carry go to err21 | |||
02021: | ln25: | c + 1 -> c[s] | Prepare registers to compute pseudo quotients q0 q1 q5 | |
02022: | ln26: | a -> b[w] | ||
02023: | jsb eca22 | Call the "eca" routine to compute qj | ||
02024: | a - 1 -> a[p] | complementation | ||
02025: | if no carry go to ln25 | one more time for the same constant | ||
02026: | a exchange b[wp] | |||
02027: | a + b -> a[s] | # of SR x 2 | ||
02030: | if no carry go to ln24 | test for 5 constants only | ||
02031: | 7 -> p | Part 2 : pseudo multiplications to compute ∑ qj ln (1 + 10 j) | ||
02032: | jsb pqo23 | |||
02033: | 8 -> p | |||
02034: | jsb pmu22 | q5 ln(1.00001) | ||
02035: | 9 -> p | |||
02036: | jsb pmu21 | q4 ln(1.0001) | ||
02037: | jsb lncd3 | S2 S5 S9 | ||
02040: | 10 -> p | ln 1 0 1 | ||
02041: | jsb pmu21 | q3 ln(1.001) | log 1 1 0 | |
02042: | jsb lncd2 | ex 1 0 1 | ||
02043: | 11 -> p | xy 1 0 0 | ||
02044: | jsb pmu21 | q2 ln(1.01) | ||
02045: | jsb lncd1 | |||
02046: | jsb pmu21 | q1 ln(1.1) | ||
02047: | jsb lnc2 | |||
02050: | jsb pmu21 | q0 ln 2 | ||
02051: | jsb lnc10 | load ln 10 | ||
02052: | a exchange c[w] | |||
02053: | a - c -> c[w] | final sum, result in C | ||
02054: | if b[xs] = 0 | Exponent processing | ||
02055: | then go to ln27 | |||
02056: | a - c -> c[w] | |||
02057: | ln27: | a exchange b[w] | ||
02060: | ln28: | p - 1 -> p | p=12 on entry (nrm27) | |
02061: | shift left a[w] | SL A (A = X) | ||
02062: | if p # 1 | to prepare A and have A sign in 12th position | ||
02063: | then go to ln28 | and A exp digits in 11 and 10th positions | ||
02064: | a exchange c[w] | A -> C | ||
02065: | if c[s] = 0 | test sign of exponent | ||
02066: | then go to ln29 | |||
02067: | 0 - c - 1 -> c[m] | invert if negative | ||
02070: | ln29: | c + 1 -> c[x] | go to normalize C (mantissa in A, exponent in C) | |
02071: | 11 -> p | (eg if ln(7.3 1043) C = [04300 ] | ||
02072: | jsb mpy27 | |||
02073: | if s9 = 0 | case xy | ||
02074: | then go to xty22 | |||
02075: | if s5 = 0 | log or ln | ||
02076: | then go to rtn21 | |||
02077: | jsb lnc10 | if log -> multiply by ln(10) | ||
02100: | jsb mpy22 | return to normalize and loop. | ||
02101: | go to rtn21 | |||
02102: | exp21: | jsb lnc10 | Load ln(10) | |
02103: | jsb pre21 | prescaling x between 0 and 1 | ||
02104: | jsb lnc2 | pseudo division by repeating subtraction to find the qj | ||
02105: | 11 -> p | load ln(2) | ||
02106: | jsb pqo21 | compute q0 by pseudo division | ||
02107: | jsb lncd1 | load ln(1.1) | ||
02110: | 10 -> p | |||
02111: | jsb pqo21 | compute q1 by pseudo division | ||
02112: | jsb lncd2 | load ln(1.01) | ||
02113: | 9 -> p | |||
02114: | jsb pqo21 | compute q2 by pseudo division | ||
02115: | jsb lncd3 | load ln(1.001) | ||
02116: | 8 -> p | |||
02117: | jsb pqo21 | compute q3 by pseudo division | ||
02120: | jsb pqo21 | load ln(1.0001) compute q4 by pseudo division | ||
02121: | jsb pqo21 | load ln(1.00001) compute q5 by pseudo division | ||
02122: | 6 -> p | precision rectification to avoid algorithm flaw e ln(2.02) = 2 | ||
02123: | 0 -> a[wp] | |||
02124: | 13 -> p | Part 2 : Pseudo multiplication | ||
02125: | b exchange c[w] | |||
02126: | a exchange c[w] | |||
02127: | load constant 6 | |||
02130: | go to exp23 | ex = (1 + r) Π ( 1 + 10-j) qj | ||
02131: | pre23: | if s2 = 0 | Trigo prescaling | |
02132: | then go to pre24 | Θ - 2 π by repeated subtractions until remainder is between 0 and 2 π | ||
02133: | a + 1 -> a[x] | |||
02134: | pre29: | if a[xs] >= 1 | ||
02135: | then go to pre27 | |||
02136: | pre24: | a - b -> a[ms] | Sub in loop | |
02137: | if no carry go to pre23 | |||
02140: | a + b -> a[ms] | Restore one step too far | ||
02141: | shift left a[w] | Save accuracy | ||
02142: | c - 1 -> c[x] | # of decades in C | ||
02143: | if no carry go to pre29 | |||
02144: | pre25: | shift right a[w] | ||
02145: | 0 -> c[wp] | |||
02146: | a exchange c[x] | |||
02147: | pre26: | if c[s] = 0 | common part for prescaling trigo end ex | |
02150: | then go to pre28 | |||
02151: | a exchange b[w] | if sign is negative | ||
02152: | a - b -> a[w] | exchange A and C, A scaled argument B = ln(10) | ||
02153: | 0 - c - 1 -> c[w] | invert sign | ||
02154: | pre28: | shift right a[w] | Align A then follow thru pqo to compute pseudo quotients. | |
02155: | pqo23: | b exchange c[w] | Part of pseudo multiplication | |
02156: | 0 -> c[w] | This routine is use by ln, ex, direct and inverse trigov (s2=0) | ||
02157: | c - 1 -> c[m] | |||
02160: | if s2 = 0 | |||
02161: | then go to pqo28 | |||
02162: | load constant 4 | The rest of the code use to forge constants with repeated patterns (see note) | ||
02163: | c + 1 -> c[m] | |||
02164: | if no carry go to pqo24 | |||
02165: | pqo27: | load constant 6 | ||
02166: | pqo28: | if p # 1 | ||
02167: | then go to pqo27 | |||
02170: | shift right c[w] | |||
02171: | pqo24: | shift right c[w] | ||
02172: | nrm26: | if s2 = 0 | Use by ln or trigo | |
02173: | then go to rtn21 | trigo (S2=0) goto rtn21 | ||
02174: | return | and test S1 to do a "rtn12" or a "return" | ||
02175: | lncd2: | 7 -> p | Forge constant see note | |
02176: | lnc6: | load constant 3 | load part of constant | |
02177: | load constant 3 | ln(1.001) = 0.999500330850 | ||
02200: | load constant 0 | |||
02201: | lnc7: | load constant 8 | ||
02202: | load constant 5 | |||
02203: | load constant 0 | |||
02204: | load constant 9 | |||
02205: | go to lnc9 | |||
02206: | exp29: | jsb eca22 | ex processing by pseudo multiplication (called by exp21) after computation of pqj | |
02207: | a + 1 -> a[p] | |||
02210: | exp22: | a -> b[w] | ||
02211: | c - 1 -> c[s] | |||
02212: | if no carry go to exp29 | loop for the next call to eca22 | ||
02213: | shift right a[wp] | A/10 | ||
02214: | a exchange c[w] | |||
02215: | shift left a[ms] | next qj | ||
02216: | exp23: | a exchange c[w] | entry point | |
02217: | a - 1 -> a[s] | dec # of SR for a constant | ||
02220: | if no carry go to exp22 | test for a next cst | ||
02221: | a exchange b[w] | No the end | ||
02222: | a + 1 -> a[p] | Correct mantissa | ||
02223: | jsb nrm21 | Goto normalize | ||
02224: | rtn21: | select rom 1 | relay to "rtn11" | |
02225: | eca21: | shift right a[wp] | Exponent calculation in | ( 1 + 10-j) qj |
02226: | eca22: | a - 1 -> a[s] | ||
02227: | if no carry go to eca21 | do the #of SR for a cst | ||
02230: | 0 -> a[s] | |||
02231: | a + b -> a[w] | thA = A +B == B 1.01 if 2 SR | ||
02232: | return | |||
02233: | pqo21: | select rom 1 | relay to "pqo11" | |
02234: | pmu21: | shift right a[w] | ||
02235: | pmu22: | b exchange c[w] | Entry point for ln part 2 (pseudo multiplication) | |
02236: | go to pmu24 | |||
02237: | pmu23: | a + b -> a[w] | do the PM | |
02240: | pmu24: | c - 1 -> c[s] | dec count | |
02241: | if no carry go to pmu23 | |||
02242: | a exchange c[w] | keep accuracy | ||
02243: | shift left a[ms] | |||
02244: | a exchange c[w] | |||
02245: | go to pqo23 | another cst ? | ||
02246: | mpy21: | 3 -> p | Came from 01245 "mpy11" (pqo) | |
02247: | mpy22: | a + c -> c[x] | ||
02250: | a - c -> c[s] | |||
02251: | if no carry go to div22 | entry point for "div" | ||
02252: | 0 - c -> c[s] | |||
02253: | div22: | a exchange b[m] | ||
02254: | 0 -> a[w] | |||
02255: | if p # 12 | |||
02256: | then go to mpy27 | |||
02257: | if c[m] >= 1 | if FP of X > 0 then div23 | ||
02260: | then go to div23 | |||
02261: | if s1 = 0 | else blinking display | ||
02262: | then go to err21 | |||
02263: | b -> c[wp] | separate FP and Y | ||
02264: | a - 1 -> a[m] | and prepare A for repeated subtractions | ||
02265: | c + 1 -> c[xs] | C holds the count | ||
02266: | div23: | b exchange c[wp] | ||
02267: | a exchange c[m] | |||
02270: | select rom 1 | go to 01271 | ||
02271: | lnc2: | 0 -> s8 | load ln(2) | |
02272: | load constant 6 | 0.6931471 | ||
02273: | load constant 9 | |||
02274: | load constant 3 | |||
02275: | load constant 1 | |||
02276: | load constant 4 | |||
02277: | load constant 7 | |||
02300: | load constant 1 | |||
02301: | go to lnc8 | +0.000000080553 | ||
02302: | pre27: | a + 1 -> a[m] | Part of prescaling for ex | |
02303: | if no carry go to pre25 | |||
02304: | myp26: | a + b -> a[w] | Use by addition (place saving) | |
02305: | mpy27: | c - 1 -> c[p] | ||
02306: | if no carry go to myp26 | |||
02307: | mpy28: | shift right a[w] | ||
02310: | p + 1 -> p | Normalization process | ||
02311: | if p # 13 | |||
02312: | then go to mpy27 | Right scaling loop | ||
02313: | c + 1 -> c[x] | |||
02314: | nrm21: | 0 -> a[s] | FP in A | |
02315: | 12 -> p | EXP in C | ||
02316: | 0 -> b[w] | Left scaling | ||
02317: | nrm23: | if a[p] >= 1 | SLA and adjust EXP | |
02320: | then go to nrm24 | |||
02321: | shift left a[w] | |||
02322: | c - 1 -> c[x] | |||
02323: | if a[w] >= 1 | |||
02324: | then go to nrm23 | Left scaling loop | ||
02325: | 0 -> c[w] | |||
02326: | nrm24: | a -> b[x] | ||
02327: | a + b -> a[w] | |||
02330: | if a[s] >= 1 | |||
02331: | then go to mpy28 | Fractionoverflow ? | ||
02332: | a exchange c[m] | If yes go to right scaling | ||
02333: | c -> a[w] | result in C | ||
02334: | 0 -> b[w] | zap B | ||
02335: | nrm27: | 12 -> p | point to 12 | |
02336: | go to nrm26 | back to the farm via "rtn21" (S1=0) or "return" | ||
02337: | lncd1: | 9 -> p | load ln(1.1) | |
02340: | load constant 3 | =0.99531017980553 | ||
02341: | load constant 1 | |||
02342: | load constant 0 | |||
02343: | load constant 1 | |||
02344: | load constant 7 | |||
02345: | load constant 9 | |||
02346: | lnc8: | load constant 8 | ||
02347: | load constant 0 | |||
02350: | load constant 5 | |||
02351: | load constant 5 | |||
02352: | lnc9: | load constant 3 | ||
02353: | go to nrm27 | to to normalize | ||
02354: | pre21: | a exchange c[w] | Entry point for prescaling of ex | |
02355: | a -> b[w] | between 0 <= x < 1 | ||
02356: | c -> a[m] | |||
02357: | c + c -> c[xs] | repeated subtractions of ln(10) | ||
02360: | if no carry go to pre24 | if positive "pre24" | ||
02361: | c + 1 -> c[xs] | if negative | ||
02362: | pre22: | shift right a[w] | ||
02363: | c + 1 -> c[x] | |||
02364: | if no carry go to pre22 | |||
02365: | go to pre26 | go to "prev26" to finish | ||
02366: | lnc10: | 0 -> c[w] | load ln(10) | |
02367: | 12 -> p | 2.3025 | ||
02370: | load constant 2 | |||
02371: | load constant 3 | |||
02372: | load constant 0 | |||
02373: | load constant 2 | |||
02374: | load constant 5 | |||
02375: | go to lnc7 | + .00008509 | ||
02376: | lncd3: | 5 -> p | ||
02377: | go to lnc6 | + .000000003 |
J. LAPORTE
12 November 2006
Note : How
to forge constants when there is no room in Rom.
Only 3 constants
- π/4 = tan-1
= arc tan(1) = 0.7853981635
- ln (2) = 0.693147190553
- and ln(10) = 2.302585093
are loaded
completely from rom.
The rest is forged (I used this term instead of calculated which makes no sense
regarding a constant).
The pattern found in the ln(x) constants and in the angles in radians are
singular: digits 6 and 9 are often used.
1) Inverse trigo
arc tan(0.1) =
0.099668652496
arc tan(0.01) = 0.0099996666866
arc tan(0.001) = 0.000999999666
arc tan(0.0001) = 0.000099999999666
2) ln and e^x
ln(1.00001) =
0.999995
ln(1.0001) = 0.9995
ln(1.001) = 0.999500330850
ln(1.01) = 0.99503085093
ln(1.1) = 0.09531017980553
The relevant code is at 02156
02156: | 0 -> c[w] | |
02157: | c - 1 -> c[m] | |
02160: | if s2 = 0 | |
02161: | then go to pqo28 | |
02162: | load constant 4 | |
02163: | c + 1 -> c[m] | |
02164: | if no carry go to pqo24 | |
02165: | pqo27: | load constant 6 |
02166: | pqo28: | if p # 1 |
02167: | then go to pqo27 | |
02170: | shift right c[w] | |
02171: | pqo24: | shift right c[w] |
The scheme is
identical: register C is zapped and C=0
Then c 1 -> c[m] makes C=09999999999000
Pointer p is positioned load constant 4 makes C=09999499999000
And c + 1 -> c[m] finish the job C=09999500000000,
(this is the case of ln(1.00001).
Sometimes this scheme is completed with a few figures loaded from rom, eg.
Ln(1.01) : C=00995033085093.
The case of const arc tan 0.001 and arc tan 0.0001 is simpler only digits 9
and 6!
That was surely a space saving strategy, but when memory was not so sparse : this code remain (in the Woodstock machines for example).