PiDP-8/I Software

View Ticket
Log In
Ticket Hash: f1bd669e8dfc5cfefb0b8f7c17b0015a20073516
Title: Port the PiDP-8/I specific features to SIMH's front panel API
Status: Closed Type: Feature Request
Severity: Important Priority: High
Subsystem: front panel Resolution: Implemented
Last Modified: 2017-02-28 18:04:29
8.42 years ago
Created: 2016-11-22 01:23:17
8.69 years ago
Version Found In:
User Comments:
tangent added on 2016-11-22 01:23:17:
The current PiDP-8 code is a fairly heavily modified version of the SimH PDP-8 simulator, with all of the PiDP-8 code intermingled with the SimH code. The [SimH front panel API](https://github.com/simh/simh/blob/master/sim_frontpanel.h) may allow all or most of this code to be extracted to a separate program, allowing us to update SimH separately without re-patching SimH each time we upgrade.

If any modifications must be made to SimH (e.g. to support nonstandard peripherals, such as the LAB-8/e extensions to the PDP-8/e) it should be done in separate modules, so that the core simulator is modified as little as possible.

Ideally, the front panel SimH client app would run asynchronously from the core simulator, each concentrating on their separate concerns, merely exchanging messages to coordinate behavior.

tangent added on 2016-12-07 05:02:09:
The original work appears to have been done sometime before July 25, 2015, based on a post by Mike Barnes (the one credited for the work in AUTHORS.md) in the "Serial Terminal for PiDP-8" thread.

tangent added on 2016-12-11 02:35:58:
This work may solve another problem, that being that the current LED lamp simulator code depends on the speed at which it gets updates from the simulator core. If you slow the simulator down (e.g. `set throttle 333k` to make it mimic the speed of a real PDP-8/I) the panel display flickers badly.

By moving this code out into a separate process, we will force a move toward a model where the speed of updates doesn't matter so much as the *time* between each lamp change.

A simple way to model it would be a circular buffer of boolean flags per LED, one for the LED's state at each millisecond over the last, say, 500ms. (Justification below.)

When we get a state change for a given LED, we save that value to a flag. Then, a 1ms interval timer pushes whatever value is in that flag for each LED into the queue for that LED's queue.

The LED updater then accumulates the on-time for each lamp by counting the 1s in the queue, scaled by their distance back in the queue. This decay function is chosen such that a string of 1s at the front of the queue >= 60ms gives full brightness to emulate the warm-up delay for an incandescent lamp. The same function ensures that if the 60ms "on" pulse is at the tail end of the queue, the lamp will be nearly dark, and fully dark if the next 60ms go by without the lamp getting a "1" again.

This scheme requires a lot of math on each update, but it only updates once every 1ms. It may be possible to slow it to once every 10-17ms, giving a 60-100 Hz update rate. The 60ms and 500ms values are obviously also tunable.

tangent added on 2016-12-11 04:21:41:
We need to run this at least 2x faster than the panel update rate to avoid aliasing, and probably more like 4-5x, according to the standard DSO sample rate rules. So, let us assume the update frequency is 1-3ms.

tangent added on 2016-12-11 06:49:06:

Let us start with the assumption that y = 1 - x² is a good approximation for the decay function for the brightness of an incandescent lamp. That is, 100% at the start, falling off increasingly quickly toward 0% brightness. The exact decay function does not affect the argument.

Now imagine that we are sampling the LED value at 1ms increments over a 100ms window. When our simulator changes the state of an LED, we save that new value, and then every 1ms thereafter, we store a 1 or 0 into a circular buffer for that LED representing an on or off state for that LED at that time. If the LED stays on continuously for 100ms, we'll end up with a buffer full of 1s, whereas if it changes state between each sample time, we'll get a buffer half full of 1s interleaved with 0s.

If we want the integral over that function to equal 1 (100% brightness) over the sample window, we need to scale the decay function by some factor z, which value we can get on the Pi directly via Mathematica:

Solve[Integrate[z*(1 - x^2), {x, 0, 1}] == 1, z]

z turns out to be 1.5 for that decay function.

Now imagine that our vector of 0s and 1s act as a Boolean mask over the decay function: where there is a 1, we get the value of the function at that point, and where there is a 0 we get nothing. If all the samples are 1, we get a staircase approximation of the decay function, like the traditional way of introducing integration in calculus. Where the samples are 0, we are missing treads in our staircase.

If we numerically integrate that chopped-up function, we get the brightness value, such that it takes 100ms of solid 1s to get 100% brightness out, and any lesser proportion of 1s gives a dimmer output, with skews toward the present giving brighter outputs than skews toward the past. (i.e. More recent 1s have greater effect on the output.)

This idea is demonstrated in this C++ program.


tangent added on 2016-12-26 21:10:40:
The update to the latest version of SIMH is now complete, as of [9025c14510], so renamed this item to just cover the front panel API port.

tangent added on 2017-01-13 07:29:35:
Although the design above is given in terms of floating-point numbers, we should try to implement it via an integer approximation method, possibly even a vectorized version if we can find a decent library for doing such things portably. Ideally, this new implementation should be more efficient than the current incandescent lamp simulator, with the goal of making it run with low impact even on Pi 1s.

tangent added on 2017-01-14 09:55:06:
Found another reason to do this: when throttling the simulator down, the display update rate goes down under the incandescent lamp simulator, too, because 100% of the code for the lamp simulator isn't on the GPIO side. It depends on a certain iteration rate of the CPU instruction loop to keep the display fed.

This new design would entirely decouple the display update code from the LED update events. If no new events come in, we still sample it at the prior state.

tangent added on 2017-02-28 18:04:29:

Closing this as too much of the above is now implemented, rejected, or otherwise outdated.

Replaced it with [a new, focused ticket].