Monthly Archives: December 2014

The HP-67 emulator, completion of the transition to the new API

At this point, all that remains is to write an engine that understands the new API.  It should push preformatted strings to the UI, tell it to repaint itself, and ask it for input, then pass the returned input back to the keypress-handling function.

The code for this is fairly straight-forward.  It does have to keep track of old values of some state variables, so that it can know when to update state in the UI and when not to.  The code looks like this:
engine.lisp

(defun run-engine (ui &key (stacksize 4))
  (let* ((stack (get-new-stack-object stacksize))
         (mode (get-new-mode-object))
         (prev-active-mode nil)
         (prev-complex-mode nil)
         (prev-display-mode nil)
         (prev-display-digits -1))

    (do (quit-requested)
        (quit-requested)

      (let ((current-active-mode (modes-run/prog mode))
            (current-complex-mode (modes-complex mode))
            (current-error-text (stack-error-state stack))
            (current-display-mode (modes-display-mode mode))
            (current-display-digits (modes-digits mode)))

        (unless (eq prev-active-mode current-active-mode)
          (hp67-ui:ui-set-active-mode ui current-active-mode)
          (hp67-ui:ui-set-active-keys ui (get-key-structs current-active-mode
                                                          :limit-to-mode t))
          (setf prev-active-mode current-active-mode))

        (unless (and (eq current-display-mode prev-display-mode)
                     (= current-display-digits prev-display-digits))
          (hp67-ui:ui-set-display-mode ui current-display-mode current-display-digits)
          (setf prev-display-mode current-display-mode
                prev-display-digits current-display-digits))

        (unless (eq prev-complex-mode current-complex-mode)
          (hp67-ui:ui-set-complex-mode ui current-complex-mode)
          (setf prev-complex-mode current-complex-mode))

        (when current-error-text
          (hp67-ui:ui-set-error-text ui current-error-text))

        (let ((num-stack-to-pass (if (> stacksize 0)
                                     stacksize
                                     (length (stack-registers stack)))))

          (hp67-ui:ui-clear-stack-contents ui num-stack-to-pass)
          (dotimes (i num-stack-to-pass)
            (let ((entry (nth i (stack-registers stack))))
              (unless entry
                (setf entry 0))
              (hp67-ui:ui-add-stack-value ui i
                                          (format-for-printing mode entry)))))

        (let ((mem (stack-memory stack)))
          (hp67-ui:ui-clear-memory-contents ui)
          (dotimes (i (length mem))
            (hp67-ui:ui-add-memory-value ui i
                                         (car (nth i mem))
                                         (format-for-printing mode (cdr (nth i mem))))))

        (hp67-ui:ui-paint ui)
        (let ((response (hp67-ui:ui-get-input ui)))
          (setf quit-requested (hp67-ui:get-quit-requested ui))
          (unless quit-requested
            (etypecase response
              (string
               (when (string= response "")
                 (setf response "enter"))
               (handle-one-keypress response nil nil stack mode
                                    :arg-is-num t))
              (key-struct
               (handle-one-keypress (key-struct-abbrev response)
                                    nil nil stack mode
                                    :arg-is-num nil)))))))))

This is checked into the git repository under the tag v2014-12-04.  Also, I just realized that my tags hadn’t been going to the repository.  They have to be explicitly pushed, by supplying the –tags switch to the git push command.

The HP-67 emulator, moving the ncurses interface to a new API model

We had a basic calculator running, but it had too much understanding of the interior functioning of the calculator.  We’ve changed this now, there is a UI base class from which user interfaces might derive.  This base class is given preformatted strings for things like stack contents and memory values, leaving the user interface code to concentrate solely on rendering those things it is told are there.  The user interface doesn’t have to know how the stack is laid out, or how to convert stack values to strings, that’s all handled in the internals in this new model.

We’ve defined generic functions on the ncurses class for three generic function definitions.  There’s an :after method on ui-set-active-mode, because the ncurses interface will modify its screen layout depending on what mode (interactive, programming, executing) is currently active.  We’ve also defined functions for ui-paint and ui-get-input, neither of which has an implementation for the base class.

We haven’t put everything together yet, because now the engine has to be rewritten to understand the new API.  Once that’s done, we’ll reassemble the calculator and test.

The current code is checked into the git repository under the tag v2014-12-03.