I’ll call my simulator of the HP97 the IQ97 (see if you can decipher the pun). Despite my effort at self-restraint, this has bloomed into a full-blown obsessive-compulsive sample work, as I can’t stop thinking of new things to do with it. So, rather than waiting until it’s all done, since there is no way to know when that will be, I’ll blog it out as I go along. A tremendous amount of interesting technology that deserves to be shown off has already gone into it. You’ll forgive the bits that get finished or undone later in the spirit of joyful bodging, and you’ll overlook messy bits that were just hacked together to get to something more interesting. And bugs? Please post or email ‘em when you find ‘em.
You’ll need the current VB9 Customer Technology Preview (CTP). Then you will need to copy the database Saturn5_002.mdf file and Saturn5_002_log.LDF file to c:\ , the root of your C drive. (This pun is too obscure to guess: one version of the chip in the HP calculators was called the ‘Saturn’ and the rocket used to launch the Apollo-Soyuz mission was the mighty Saturn 5). Finally, you will need this zipped VB9 project directory tree. You should be able to open up the solution, hit F5, and follow along these blogs. At this point, if you have things properly installed, you will see quite a lot of console output fly by, ending with the last few lines of the calculator’s diagnostic program:
...
219 SCI
220 PRTX
221 DSP1
222 FIX
223 PRTX
224 R/S
=000:=>
Most of the console output consists in disassembled calculator-key functions, about which we will have more to say later on.
There are lots of things to do in console mode, but, for now, just type m, which stands for ‘monitor,’ and <Enter>, and you should see the faceplate of the IQ97. You have now exited console mode: all keyboard and mouse entry go into the UI until you type q at the faceplate. That will send you back to console mode. For some reason I don’t understand, you must force the UI into focus the first time you type ‘m<Enter>;’ the easiest way to do that is just click anything on the faceplate or just Alt-tab over to its window. After the first time, you can m<Enter> and q back and forth and focus will track correctly.
Hewlett-Packard’s diagnostic program is preloaded, so if you click the ‘A’ button on the extreme left center or just type a (no <Enter> on the faceplate), you should see blinking lights for about 5 to 10 seconds, halting with 0.00 in the main display at top-left; -8.889e-088 twice and 0.00 on the paper tape, right-center top; and, in the machine-state display in green, left-center top, ip = 224, cmd = R/S, ic = 1018, TIME = something reasonable around 5 to 10 seconds, and ips (instructions per second) = something reasonable around 250 or below. If you get all that, you’ve passed the basic diagnostics and you are ready to use the calculator.
The UI mode responds to mouse clicks, of course, but also to several standard computer keyboard presses, like the number keys, the arithmetic keys, <End> for the calculator ENTER key (because the computer <Enter> key automatically mouse clicks whatever button has the focus, it was difficult to use that for the calculator ENTER key), <Backspace> for CLX, A-E for the A-E keys, F for the calculator f-shift key (the glaring yellow one), and R for Rv, which rolls the stack down one.
To get off the ground, type 1 <End> 2 +, and you should see 3.00 in the main display and the X slot of the stack display center-top. The main display is always an echo of the X slot. If you just finished the diagnostics, the rest of the stack will contain Y=0.00, Z=300.00, and T=300.00. For those not familiar with the famous Hewlett-Packard Reverse-Polish Notation (RPN), it’s just postfix immediate. Numeric keys build up inputs, so, when we type the first 1, we start input mode. When we type <End>, we end input mode. Typing 2 causes an automatic push (HP calls it stack lift) of 1 into the Y slot, and puts the machine back into buildup mode . + Terminates buildup mode and immediately executes Y+XàX, dropping the stack down, autocopying T into Z. Most of the rest of the calculator functions operate this way, and a little bit of playing around will get you familiar and at-ease with it if you aren’t already.
While RPN eventually lost out to so-called ‘algebraic mode’ with parentheses in the calculator mass market, presumably because students preferred to slavishly copy formulas from textbooks to their calculators rather than to read and understand the formulas, RPN really is technically superior and much easier to grasp, not to mention much easier to edit and correct. Yes, there is a teeny-tiny learning molehill, but the benefits massively outweigh the cost, especially for anyone who has actually studied computers for a little while. Be that as it may, RPN was introduced by HP for pure expediency: it required less precious, on-board memory than the algebraic style. RPN only requires the value stack because all operations execute immediately. An algebraic-mode calculator must store a complete expression tree as it is built up, and would thus have been intolerably limited in 1976, when 25 memory cells was considered lavishly, luxuriously, many. With algebraic mode, users had to resort to pencil-and-paper to break up big formulas, while, with a little practice, an experienced RPN user could key in the most hairy formula right the first time (hint: start from the innermost subexpressions and build OUT, just as with applicative order or call-by-value in programming languages.
If you want to play with the primes program, go to the drop-down combo box just under the main display and select “Primes” (don’t bother with “Moon Lander” – it’s not working yet). Now TAB out of the combo box to remove focus from it (another UI booger – sorry). Click or type E to enter Prime’s Auto Mode, and type 100 B 200 C D to generate all primes from 100 to 200. This should go for about 20 to 40 seconds, with 4788 instructions executed at 125-250 ips, and leave results on the scrollable paper tape. If you want to heat up your computer for a few hours, type 1000000000 B 1000001000 C D and it will spit out one prime about once every 10 to 20 minutes. I suggest you leave that for overnight, though, and try out the console mode first, without the UI, because console mode has about 1000 times greater throughput.
Type q to quit the UI (you can get it back with another m<Enter>), and enter the following at the console prompt, each line terminated with <Enter> (don’t press <Enter> too early – it single-steps the program in the calculator, but, if you do, I’ll let you guess how to get started again):
| read.prim | an extended command, not really part of the HP97 command set |
| T | Turns of trace mode |
| E | Turns on auto-print mode, lest the calculator stop after each prime |
| 1 | Simulates pressing of the 1 key on the calculator |
| 0 | Yes, we have to enter each digit on its own |
| 0 | As if it were an individual calculator key press |
| 0 | Each followed by the Enter key, so, yes, console mode is clumsy |
| 0 | But it will execute much, much faster, when we run the program |
| 0 | So hang in there for four more zeros, ok? |
| 0 | Three … |
| 0 | Two … |
| 0 | One … |
| 0 | Liftoff! Yay, we’re done (and it sounds like a rocket launch) |
| B | Tells the calculator that we have the starting prime |
| 1 | Oh, no, here we go again! |
| 0 | Ok, go with me for five zeros |
| 0 | Four … |
| 0 | Three … |
| 0 | Two … |
| 0 | One … |
| 1 | A break from the monotony |
| 0 | Just three more and we’re done for good, I promise |
| 0 | But now I’m just funning with you because you got the idea |
| 0 | Way back there |
| C | Tells the calculator that we have the ending prime |
| D | Let ‘er rip! |
This should start producing primes immediately, starting with 1000000007, and ending with 1000000993, at the rate of 1 every few seconds, executing exactly 8,628,918 instructions at the rate of up to 225,000 instructions per second. If you did the same in UI mode, the number of instructions will be exactly the same, but the ips will be more like 225, taking well over one hour.
The primes program has another nifty feature: it will also factor any number in the X register if you click the A key (or type A<Enter> at the console-mode prompt, or just A at the UI). Make sure you’re in auto-print mode, that is, that you have toggled E until 1 shows in the X slot and the main display.
At the console prompt, you can also type preg to print out the registers and prst to print out the stack, or go back into trace mode with t, all followed by <Enter>, of course, which I won’t bother to mention for console mode from now on.
If something goes wrong and the program becomes unresponsive (called ‘going into the weeds’ – a metaphor from automobile racing) just kill it from Visual Studio and start it over. It’s just a blogging sample, don’t you know, and hasn’t been subjected to anything like product-level QA.
Ok, but guess what? You’ve stuck with me through all that and we are ready for our first REAL TREAT of VB9 programming! Look at the code for intercepting computer keystrokes in the UI panel. It’s in Monitor.vb and is called Monitor_KeyPress. This is the ordinary winforms event handler for KeyPress, which gets forwarded to the topmost control—the window itself—from whatever subcontrol happens to have the focus.
Private Sub Monitor_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
Dim k = Char.ToUpperInvariant(e.KeyChar)
If k >= "0"c And k <= "9"c Then
CObj(Me).("Num" & k & "_Click")(Nothing, Nothing)
ElseIf k >= "A"c And k <= "E"c Then
CObj(Me).(k & "Key_Click")(Nothing, Nothing)
ElseIf k = "F"c Then
fShiftKey_Click(Nothing, Nothing)
ElseIf k = "+"c Then
Me.PlusKey_Click(Nothing, Nothing)
ElseIf k = "-"c Then
Me.MinusKey_Click(Nothing, Nothing)
ElseIf k = "*"c Then
Me.TimesKey_Click(Nothing, Nothing)
ElseIf k = "/"c Then
Me.DivKey_Click(Nothing, Nothing)
ElseIf k = "R"c Then
Me.RollDownKey_Click(Nothing, Nothing)
ElseIf k = "Q"c Then
fMonitor.Dispose()
fMonitor = Nothing
End If
End Sub
We’ll convert the key to uppercase and start testing its value. The first branch runs when you press any key in the 0 through 9 range, and … what’s that weirdness? Method call through dynamic Identifiers! The idea is rather than specifying the method to call for each digit key statically, like this
If k = "0"c Then
Me.Num0_Click(Nothing, Nothing)
ElseIf k = "1"c Then
Me.Num1_Click(Nothing, Nothing)
ElseIf k = "2"c Then
Me.Num2_Click(Nothing, Nothing)
...
and so on, let’s just calculate the name of the method to call from the keycode. It only works through late binding (can’t statically bind to a method name that is not known until run time), so we first must convert the method receiver to something of static type Object, that is, convert Me to an object (hopefully, this syntax noise will be eliminated in the next drop of VB9):
CObj(Me)
Then, let’s calculate the name of the button-click event-handler method to call:
"Num" & k & "_Click"
(the ampersand glues strings – and things automatically convertible to strings – together), put that method name inside parentheses to the right of the dot:
CObj(Me).("Num" & k & "_Click")
(that's the magic syntax that means “dynamic identifier!”), supply the arguments to finish our first method-call through dynamic identifier:
CObj(Me).("Num" & k & "_Click")(Nothing, Nothing)
So, pressing a digit key on the computer keyboard just forwards execution to the exact code that process a digit-key button click through the computer mouse, just as we want. There is another method call through dynamic identifiers for handling A through E. Notice we cannot make the all-too-easy typing error of getting the digit wrong in the method name, so the dynamic identifiers also give us prophylaxis against cut-and-paste coding bugs!
We could not conveniently do likewise with the arithmetic keys. While I would have liked to say
Private Sub [+_Click](...) Handles PlusKey.Click
which VB9 accepts, by the way, because of the bracket escape syntax, the CLR balks at a name such as +_Click for a method and it fails to link. But if I had been able to write that, then the four lines for processing arithmetic keyboard input would have collapsed to just one method call through dynamic identifiers.
That’s enough for today, but in future installments, you will see much more use of this feature, as well as calls through dynamic-method delegates and through lambda-expression delegates. Also in the future, I will update the IQ97 source solution as I go along, and each blog will tell you whether there is a new drop to pick up.