In previous HP-67 emulators that I wrote in other languages, the UI was always keyboard-based, and numbers did not have to be assembled from individual number tokens, the user simply typed the number out on the line and that was passed to the program for parsing.
I want to allow for the possibility that this new emulator will have a GUI front end with key-presses that behave like the calculator does, so keys that are used to assemble numbers must be treated in a special manner.
When the engine receives a keypress, it checks to see if the key is a token key before attempting to run the form attached to the key. If it is a token key, that token is retrieved and handed to the stack object, which now has a token assembler structure that acts on these contents. It then switches the mode of the program to :NUMERIC-INPUT. This mode switch has only one noticeable effect: it changes what the CLX key does. When CLX is pressed in any other context, it clears the X register. In numeric-input mode, after at least one input token has been seen, it clears the input line but does not touch the X register.
If the ENTER key is pressed and there is no token being assembled, it pushes a copy of X onto the stack. That code is in stack.lisp.
If a non-token key arrives when tokens have been seen and are being assembled, the in-progress number is immediately completed and pushed onto the stack before the non-token key’s operation is invoked.
Here we see two examples of execution in the current setup. Note that the registers-copy field of the stack holds the contents of the stack before the last operation occurred, so you can see there what the inputs were to the most recent operation.
Assembling a number, pushing it onto the stack. Starting a second number, but pressing the wrong key, so sending CLX to clear the error and enter the correct key:
*slime-repl sbcl*
CL-USER> (let ((stack (get-new-stack-object 4))
(mode (get-new-mode-object))
(arg-fcn #'(lambda (x)
(format t "Argument for ~A: " x)
(read))))
(handle-one-keypress "1" arg-fcn nil stack mode)
(handle-one-keypress "2" arg-fcn nil stack mode)
(handle-one-keypress "." arg-fcn nil stack mode)
(handle-one-keypress "enter" arg-fcn nil stack mode)
(handle-one-keypress "3" arg-fcn nil stack mode)
(handle-one-keypress "clx" arg-fcn nil stack mode)
(handle-one-keypress "4" arg-fcn nil stack mode)
(handle-one-keypress "+" arg-fcn nil stack mode)
stack)
#S(STACK
:REGISTERS (16 0 0 0)
:REGISTERS-COPY (4 12 0 0)
:NUM-REGISTERS 4
:LAST-X 4
:ASSEMBLER #S(TOKEN-ASSEMBLER
:MANTISSA-SIGN 1
:MANTISSA #<SB-IMPL::STRING-OUTPUT-STREAM {1006E13563}>
:MANTISSA-DIGITS 0
:EXPONENT-SIGN 1
:EXPONENT #(0 0)
:EXPONENT-DIGITS 0
:TRANSLATION ((:|0| . "0") (:|1| . "1") (:|2| . "2")
(:|3| . "3") (:|4| . "4") (:|5| . "5")
(:|6| . "6") (:|7| . "7") (:|8| . "8")
(:|9| . "9") (:DOT . ".") (:EEX . "d")
(:ENTER . :ENTER) (:CLX . :CLX) (:CHS . :CHS))
:SEEN-DOT NIL
:SEEN-EXPT NIL
:FINALIZED NIL)
:MEMORY NIL
:FLAGS (("0") ("1") ("2") ("3"))
:PROGRAM-MEMORY NIL
:COMPLEX-ALLOWED-P NIL
:ERROR-STATE NIL)
Starting to assemble a number, then pushing the SIN key. The number that was being assembled is finalized and pushed onto the stack so that SIN operates on it:
*slime-repl sbcl*
CL-USER> (let ((stack (get-new-stack-object 4))
(mode (get-new-mode-object))
(arg-fcn #'(lambda (x)
(format t "Argument for ~A: " x)
(read))))
(handle-one-keypress "3" arg-fcn nil stack mode)
(handle-one-keypress "0" arg-fcn nil stack mode)
(handle-one-keypress "." arg-fcn nil stack mode)
(handle-one-keypress "sin" arg-fcn nil stack mode)
stack)
#S(STACK
:REGISTERS (1/2 0 0 0)
:REGISTERS-COPY (30 0 0 0)
:NUM-REGISTERS 4
:LAST-X 30
:ASSEMBLER #S(TOKEN-ASSEMBLER
:MANTISSA-SIGN 1
:MANTISSA #<SB-IMPL::STRING-OUTPUT-STREAM {1006CA3C23}>
:MANTISSA-DIGITS 0
:EXPONENT-SIGN 1
:EXPONENT #(0 0)
:EXPONENT-DIGITS 0
:TRANSLATION ((:|0| . "0") (:|1| . "1") (:|2| . "2")
(:|3| . "3") (:|4| . "4") (:|5| . "5")
(:|6| . "6") (:|7| . "7") (:|8| . "8")
(:|9| . "9") (:DOT . ".") (:EEX . "d")
(:ENTER . :ENTER) (:CLX . :CLX) (:CHS . :CHS))
:SEEN-DOT NIL
:SEEN-EXPT NIL
:FINALIZED NIL)
:MEMORY NIL
:FLAGS (("0") ("1") ("2") ("3"))
:PROGRAM-MEMORY NIL
:COMPLEX-ALLOWED-P NIL
:ERROR-STATE NIL)
This code is in the
git repository under the tag v2014-11-18.