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:

RunLoop: ; aka $go
01754 if 1 = s 15 then goto 01707        ; if key_pressed
01756 display toggle

01757 jsb 01472                          ; fetch
; (selects a data reg and sets P based on the PC (step number))
; In : M1=20000000001202 so PC=01
; Out: data=9 P=12

01760 jsb 01772                          ; $inc
; (adds 1 to PC)
; In : M1=20000000001202 so PC=01
; Out: M1=20000000002202 so PC=02

*** ram[9] -> c (=e5e5e5e5000000)
01761 data register -> c 0               ; C= e5e5e5e5000000
; (just got program steps 01..07 to C register. Step 01 at LHS)
;
01762 if p # 0 then goto 01575
01575 shift right c[w]                   ; C= 0e5e5e5e500000
01576 p - 1 -> p                         ; P= 11
01577 if n/c goto 01762
;
01762 ...                                ; C= 00e5e5e5e50000 P=10
01762 ...                                ; C= 000e5e5e5e5000 P= 9
01762 ...                                ; C= 0000e5e5e5e500 P= 8
01762 ...                                ; C= 00000e5e5e5e50 P= 7
01762 ...                                ; C= 000000e5e5e5e5 P= 6
01762 ...                                ; C= 0000000e5e5e5e P= 5
01762 ...                                ; C= 00000000e5e5e5 P= 4
01762 ...                                ; C= 000000000e5e5e P= 3
01762 ...                                ; C= 0000000000e5e5 P= 2
01762 ...                                ; C= 00000000000e5e P= 1
01762 ...                                ; C= 000000000000e5 P= 0
01762 if p # 0 then goto 01575
01764 0 -> c[ms]
01765 display off
; (P was 12 for PC=01. First step now in C[1,0]. pgmcode "e5") 

execut:
01766 0 -> c[xs]
01767 c -> a[x]                          ; A= 200000000000e5
01770 a + 1 -> a[xs]                     ; A= 200000000001e5
01771 a -> rom address

; (now running a prgcode in the range "e0" - "ef")
; 1400 tbl1 prgcode.lhs= "e"
01436 c + 1 -> c[xs]                     ; C= 000000000001e5
01437 delayed rom 4
01440 if n/c goto 01460
02060 shift left a[x]                    ; A= 20000000000e50
02061 a exchange c[xs]                   ; A= 20000000000150 C= 00000000000ee5
02062 m2 -> c                            ; C= 00000000000000
02063 p <- 12                            ; P= 12
02064 decimal
02065 0 -> a[s]                          ; A= 00000000000150
02066 0 -> s 9
02067 a -> rom address

; (now running prgmcode "e5")
; 2000 tbl1 prgcode.rhs= "5"
02025 if n/c goto 02342
02342 select rom 3
01743 clear s                            ; S= .1.3.5..........
01744 delayed rom 0
01745 jsb 01705

$overf: ; check for overflow.
; 1. if mantissa=0, entire word=0 (0.00 EEX 0)
; 2. if xs 5-8, must have underflowed. Set to 0.0
; 3. if xs 1-4, must have overflowed. Set to 9.9999999e99
00305 ...
00273 return

01746 m2 exch c
01747 if p # 12 then goto 01752          ; =12 if overflow. stops running.
01752 if 0 = s 1 then goto 00247         ; if not running (but we are)

RunLoop:
01754 ...

What just happened? Here it is again but with a slightly higher level view:

RunLoop: ; aka $go
01754 if 1 = s 15 then goto 01707        ; if key_pressed
01756 display toggle

01757 ... ; fetch - set data reg and P from PC

01760 ... ; PC++

01761 ... ; get program steps

01762 ... ; shift right by P to get step in A[1,0]

01765 display off

execut:
01766 0 -> c[xs]
01767 c -> a[x]                          ; A= 200000000000e5
01770 a + 1 -> a[xs]                     ; A= 200000000001e5
01771 a -> rom address

(run program step)

01746 m2 exch c
01747 if p # 12 then goto 01752          ; =12 if overflow. stops running.
01752 if 0 = s 1 then goto 00247         ; if not running (but we are)

RunLoop:
01754 ...


It’s basically: get the current program step, PC++, run the step; and then back again for the next one.

It has to increment the PC (step number) before it runs the current one, so that the current one can be a “GTO …” that changes PC.

Having overflow turn off “running” is a nice feature. I don’t recall seeing that in earlier calculators.

Let’s have a look at some of the subroutines we skipped over in our first look:

Fetch

; M1[m]= PC, nxt prg step
; out data= prg mem ie 9+int((PC-1)/7)
;     P   = 12-2*((PC-1)%7) ie shifts right to prgcode
fetch:
01472 m1 -> c                            ; C= 20000000001202
01473 c -> a[w]                          ; A= 20000000001202
01474 0 -> c[w]                          ; C= 00000000000000
01475 p <- 0
01476 load constant 8                    ; C= 00000000000008 P= 13
01477 p <- 3                             ; P= 3
01500 load constant 7                    ; C= 00000000007008 P= 2 // 7 steps per register
01501 p <- 0                             ; P= 0
01502 decimal
01503 a - 1 -> a[m]                      ; A= 20000000000202 // PC-1
01504 if n/c goto 01701

; subtract 7 from a[m] until it is -ve. c[p]++ each time to ref ram[9..15]
; a[m] is 0..48 for step 01..49
01701 binary
01702 c + 1 -> c[p]                      ; C= 00000000007009 // ram[9]
01703 if n/c goto 01545                  ; can't go beyond ram[15] => 7 pgm regs
01545 decimal
01546 a - c -> a[m]                      ; A= 29999999993202 CY // (PC-1)-7 is -ve
01547 if n/c goto 01701                  ; if not -ve, add 1 to pgm reg and repeat
;
01550 c -> addr                          ; determined pgm reg. set that. (9 in our case)
01551 a exchange c[w]                    ; A= 00000000007009 C= 29999999993202
01552 c -> a[w]                          ; A= 29999999993202
;
01553 a + 1 -> a[m]                      ; A= 29999999994202 // add 1 again
01554 if n/c goto 01654
01654 p + 1 -> p                         ; P= 1
01655 p + 1 -> p                         ; P= 2 // and add 2 nibbles for ea step
01656 if n/c goto 01553                  ; repeats until we get a[m] to 0 again
;
01553 ...                                ; A= 29999999995202 P= 3, 4 // a[m]=-5
01553 ...                                ; A= 29999999996202 P= 5, 6 //     =-4
01553 ...                                ; A= 29999999997202 P= 7, 8 //     =-3
01553 ...                                ; A= 29999999998202 P= 9,10 //     =-2
01553 ...                                ; A= 29999999999202 P=11,12 //     =-1
01553 a + 1 -> a[m]                      ; A= 20000000000202 CY      //     = 0
01554 if n/c goto 01654
01555 return


The microcode starts with PC in M1. It converts PC=01..49 to 00..48. It starts with ram[9] (01476 load constant 8; then 01702 c + 1 -> c[p]) and counts up from there whilst deducting 7 steps from (PC-1) each time.

When (PC-1) is negative, it knows it has the correct prg memory. Steps 01..07 are in ram[9], 08..14 in ram[10], …, and 43..49 in ram[15].

Then, depending on “how negative” (PC-1) is, it can work out where the prgcode is in the selected ram[]. It adds 1 to the (PC-1) value and 2 to the “number of nibbles to shift by” until it gets (PC-1) back to 0.

The result is this:

Step PC-1 ram[] P
01 00 9+int( 0/7)= 9 12-2*( 0%7)=12
02 01 9+int( 1/7)= 9 12-2*( 1%7)=10
03 02 9+int( 2/7)= 9 12-2*( 2%7)= 8
07 06 9+int( 6/7)= 9 12-2*( 6%7)= 0
08 07 9+int( 7/7)=10 12-2*( 7%7)=12
09 08 9+int( 8/7)=10 12-2*( 8%7)=10
49 48 9+int(48/7)=15 12-2*(48%7)= 0

$inc

$inc: ; *** PC++
01772 decimal
01773 m1 -> c                            ; C= 20000000001202
01774 c + 1 -> c[m]                      ; C= 20000000002202
01775 m1 exch c                          ; C= 20000000001202 M1=20000000002202
01776 binary
01777 return


$inc just adds 1 to the mantissa in M1. In theory, it’ll happily go well beyond step 49 and even beyond step 224 (of a HP-67). However, instruction 01703 in “fetch” resets us if we go beyond ram[15]. It shouldn’t be too hard to add more ram to an emulator and a few extra instructions instead of 01703 to make it work past step 49. It does make one wonder how similar the HP-29 Run Loop is, given that one’s obvious evolution from the HP-25 and its 98 program steps.

$overf

There is little point exploring this subroutine here as we have 0.00 in the display and the processing for that is trivial. It will be worth looking at, after arithmetic operations that overflow or underflow. However, for the record, the 0.00 processing is:

$overf: ; check for overflow.
; 1. if mantissa=0, word=0;
00305 m2 -> c
00306 decimal
00307 p <- 0                             ; P= 0
00310 if c[m] = 0 then goto 00272
00272 0 -> c[w]
00273 return


You can see that P#12 so it doesn’t stop running due to overflow (instruction 01747).

This is part of the HP-25 topic.

It's only fair to share...Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUponDigg thisPin on PinterestEmail this to someone

Leave a Reply

Your email address will not be published. Required fields are marked *

handheld computing

css.php