How it works

The console on the previous page is three programs talking to each other, and all of their source is here. Each links into the exact lines where things happen. Text in "double quotes" in the code's comments is quoted from N. G. Leveson and C. S. Turner, "An Investigation of the Therac-25 Accidents" (IEEE Computer, July 1993). Where the paper is silent and the code had to guess, the comment says ASSUMPTION.

Tasks that share variables

The software "has four major components: stored data, a scheduler, a set of critical and noncritical tasks, and interrupt services." Three of the tasks matter here. They run at the same time and communicate only through shared variables:

In the simulator, each shared variable is read and written in its own small transaction, just as the PDP-11 tasks did. Nothing makes a sequence of those steps atomic, which is the point.

Tyler, 1986: Malfunction 54

When the operator enters the mode, the keyboard handler puts it in MEOS. {{sim:datent}} sees it, sets the machine up for that beam, and calls {{sim:magnet}} to set the bending magnets, which takes about 8 seconds. While each magnet settles, {{sim:pTime}} is supposed to watch for edits (the paper's Figure 3):

Ptime:
  repeat
    if bending magnet flag is set then
      if editing taking place then
        if mode/energy has changed then exit
  until hysteresis delay has expired
  Clear bending magnet flag
  return

The last line is the bug. "Since Ptime clears it during its first execution, any edits performed during each succeeding pass through Ptime will not be recognized." Look for it at the end of {{sim:pTime}}. An edit made after the first magnet but before the last goes into MEOS and onto the screen, and nobody reads it. If the cursor has already been to the command line once ({{sim:cursorToCommandLine}}: "the data-entry completion variable only indicates that the cursor has been down to the command line, not that it is still there"), Datent moves on to set-up with the old beam.

Meanwhile the housekeeper has turned the turntable for the new mode. {{sim:beamPhysics}} decides what the hardware does with X-ray current and no target in the beam, and {{sim:zapTheSpecimen}} records the dose. The ion chamber saturates, so the console shows "Malfunction 54" with 6 monitor units ({{src:vt100/src/console.c#poll_machine}}).

At the keyboard, that is {{src:vt100/src/console.c#accept}} (RETURN, including the quick series that "would thus complete data entry"), {{src:vt100/src/console.c#cursor_up}} (the cursor up key) and {{src:vt100/src/console.c#send_meos}}. The test that types it: {{src:vt100/test/tests.c#tyler_with_returns}}.

Yakima, 1987: Class3

{{sim:setupTest}} runs ten times a second while the machine is set up, and each pass adds one to Class3, a single byte. On every 256th pass it rolls over to zero. {{sim:lmtchk}} only calls {{sim:chkcol}}, the check that the turntable (the "upper collimator") is where the treatment needs it, when Class3 is not zero (the paper's Figure 4):

Lmtchk:
  If Class3=0 then do not enter Chkcol
  If Class3 is not 0 then enter Chkcol
Chkcol:
  If upper collimator position inconsistent with treatment
  then set bit 9 of F$mal

The operator had turned the turntable to the field-light position ({{sim:fieldLight}}). When the set button was pressed "at the precise moment that Class3 rolled over to zero", the check was skipped, set-up test saw no malfunction, and the beam fired with nothing to spread or measure it. The console showed no dose, then "flatness". The test: {{src:vt100/test/tests.c#yakima}}.

AECL's fix: "the Class3 variable is set to some fixed nonzero value each time through Set-Up Test instead of being incremented."

Why nobody at the console could tell

The console and the terminal

The console program never draws anything itself. It sends characters and VT100 control sequences down a 9600-baud line ({{src:vt100/src/serial.c#serial_get}}). Its screen processor ({{src:vt100/src/console.c#refresh}}) rewrites only what changed, and a full screen still takes about a second. The terminal parses that stream ({{src:vt100/src/vt100.c#vt_receive}}) and draws each character from the ROM, dot by dot, through the dot stretcher ({{src:vt100/src/vt100.c#vt_render}}, {{src:vt100/src/vt100.c#glyph_dots}}). Keys go the other way ({{src:vt100/src/vt100.c#vt_key}}), repeating as a VT100's did.

Where the model guesses

The paper is the most detailed public account, and it "leaves some unanswered questions". These are the places where the code fills a gap:

{{assumptions}}

All the source

Free software under the GNU Affero General Public License, version 3 or later. Download all of it as source.tar.gz; it builds with vt100/build-web.sh and GHC's WebAssembly toolchain.

{{files}}