Enter email address here to subscribe to B5500 blog posts...

Monday, April 2, 2018

retro-B5500 Emulator Release 1.05

Nigel and I are pleased to announce that version 1.05 of the retro-B5500 emulator was released on 18 March 2018. All changes have been posted to the repository for our GitHub project. The hosting site has also been updated with this release.

This is a primarily a bug-fix release, containing the collection of corrections and enhancements that have accumulated since 1.04 was released in September 2016.

Enhancements in Release 1.05

Apple Safari Pop-up Restrictions

This first item could be qualified as a bug fix, but it's really a new feature. The emulator had been operating quite well in the three major modern workstation browsers -- Google Chrome, Mozilla Firefox, and Apple Safari. I recently updated my seldom-used MacBook Air to macOS 10.13 (High Sierra), and with that update came Safari 11.0.

When I tried to run the retro-B5500 emulator in Safari after this update, the emulator crashed after opening its first pop-up window. I quickly discovered that my retro-205 and retro-220 emulators suffered the same fate. I had Safari configured to allow pop-ups, so what was the problem?

Some research revealed that this new version of Safari places additional restrictions on pop-up windows. Specifically, with pop-ups enabled it would always allow the first pop-up, but if subsequent pop-ups were opened before some amount of time had passed, Safari would inhibit the pop-up. Reports varied on the amount of time that had to elapse between opens, ranging upward from 500ms.

The emulator opens pop-ups for the Console Panel and each of its peripheral devices during its initialization. All of these elements are implemented as Javascript objects, and as part of their instantiation, each one opens, sizes, and positions its own pop-up window. Trying to schedule appropriate delays in the initialization process that instantiates these objects was going to be difficult, so I decided instead to centralize the mechanism for opening pop-ups.

Now, each object calls a common "open pop-up" function in the B5500Util.js module, passing the parameters necessary to open and configure the window. The new mechanism maintains a queue of these open requests, and inserts delays between servicing of the queued requests. If an open request fails (i.e., the Javascript window.open() function returns null), the request is reinserted at the head of the queue and the servicing delay is increased.

This new scheme works extremely well. It automatically adjusts the delay between opens based on the individual browser's behavior. The initial timeout value is 500ms and increments by 250ms on each iteration of a failed open. My tests indicate that Safari 11.0 starts making nice when the timeout reaches 1000ms. Chrome and Firefox both cruise through pop-up opens, because their open attempts do not fail; thus nothing ever gets queued and their opens proceed without delay.

Deimplement the Application Cache

The Application Cache (or AppCache) is a feature that allows a browser to download and store all of the scripts, images, and other resources for a web-based application and store them on your workstation. You can then run the application locally, without access to a network connection to the host web server. When a network connection is available, the browser checks during application initialization for updates. You would see messages in blue displayed briefly at the top of the emulator's home page reflecting this activity.

This feature seemed ideally suited to the emulator, since once it is loaded by the browser, the emulator needs no further communication with the host web server. Alas, this feature ran afoul of a segment of the developer community who thought it should be something different than what it was, some of whom were quite nasty about it. As a result, the Application Cache has been withdrawn as a draft web standard. Both Google Chrome and Mozilla Firefox have deprecated the feature in their browsers and announced that it will be removed in a future release.

In anticipation of this, I have removed from the emulator the things that enabled the Application Cache. The home page now displays a brief message indicating the feature has been deimplemented. This message will be removed in a future release. The first time you load the emulator's home page for this new release (assuming you have an active network connection), the AppCache will be deleted in your browser, but you will still be running the prior release. That is normal AppCache behavior. Simply reload the home page to use the new release.

For those who would still like to be able to run the emulator off-line, the only remaining option is to run a web server on your workstation. You only need a minimal web server in order to do this. As described in the Getting Started wiki page, I have had good success with the free, open-source mongoose web server, which is available in versions for Windows, Mac OS, and Linux/Unix.

Emulator Release Zip Archives

For those of you running your own web server, I have decided to discontinue creating zip archives of the emulator files and placing them on the project's Google Drive. Generating such an archive can be done directly on GitHub. On the project home page, look for the green Clone or download button:

GitHub Clone or Download Zip Archive
Click that button and then the Download ZIP link. You can also browse the complete list of releases from the nn releases link on the project home page and download an archive from a specific release from that list.

Console Panel Annunciator Lamps

The small annunciator lamps along the bottom of the emulator's Console Panel that show the system's I/O channel, interrupt, and peripheral device activity have a slightly different look. Before, the lamps simply appeared in white when they were lit and disappeared when they weren't. Now each annunciator shows in dark gray when not lit. This allows you to see what all of the annunciators are, and it more closely approximates the back-lit effect the annunciators are intended to emulate.

New Console Panel Annunciators
 Note that these annunciators did not exist on a real B5500 console. They are an artifact of the emulator, present simply to give you an idea of the system's activity. Their presence can be toggled on or off by clicking the Burroughs logo on the panel. The annunciator mnemonics are explained on the Using the Console wiki page.

Asynchronous Callback Management

There has always been a significant exchange of ideas, techniques, and code among my Javascript-based emulator projects. One common module among the projects is something called the setCallback() function. It it part of the mechanism that regulates performance, trying to make the emulated Processors and peripheral devices operate at the speed of a real B5500.

Like all Javascript applications running in a web browser, the emulator executes on a single thread of the Javascript run-time system. The appearance of multiple, simultaneous activities is achieved in the same way that most operating systems appear to run multiple programs at once -- by giving each activity a short time slice and switching rapidly among the slices. The way you do that in Javascript is through callback functions that are executed asynchronously. The run-time system assures that only one of these callbacks executes at a time.

User interface events (e.g., mouse clicks) and some DOM APIs use callback functions to signal the emulator, but the primary way in which the emulator regulates performance is by keeping track of when Processor activity and I/O completions should be occurring in real time and inserting delays to allow real time to catch up with emulator time. The way it does this is through the Javascript setTimeout() function. You pass it a reference to a callback function, a number representing a delay in milliseconds, and an optional list of parameters. The run-time calls that function, passing any parameters, after the specified delay. The emulator typically has several of these asynchronous function calls scheduled at a time, and their interleaved execution is what gives the appearance of multiple simultaneous activities in the system.

There are two big problems with setTimeout(), though:
  1. The delay is almost always at least a little longer than you asked for. Since everything is single-threaded, some other function may be executing when your delay expires, and there may be other functions whose delay has expired ahead of yours, waiting in the queue for their turn on the single thread. In most browsers, background activities such as screen painting and memory garbage collection also run on the single thread, so these can also inflate the delay before a callback is actually called.
  2. Modern browsers limit the minimum delay to at least four milliseconds when callbacks are scheduled out of callbacks. There are a couple of reasons for this, but the original one was to inhibit poorly-behaved web pages from engaging in intensive CPU activity that drained the batteries of laptops.
The emulator tries to be well-behaved, but both of these problems interfere with its goal of running the Processors and peripheral devices at real B5500 speeds. The 4ms limit was particularly bedeviling for Processor performance throttling, which requires delays of less than 4ms. There is no mechanism for scheduling shorter delays, but I did find one that could produce a near-zero delay -- what in operating systems is termed a yield. This is based on the Javascript postMessage() API. It involves the emulator sending a small message to itself and attaching a callback function to the onMessage event.

This discovery lead to the development of setCallback(), which is used wherever you would normally use setTimeout().  If a particular call requires a delay of at least 4ms, settCallback() uses setTimeout(); if the delay is less than 4ms, it uses postMessage/onMessage instead. In the first case, the actual delay will be somewhat longer than requested, and in the second case it will usually be much shorter. To compensate for that, once the delay expires, the mechanism measures the actual delay that occurred and accumulates the deviation between the actual delay and the one originally requested.

A portion of that cumulative deviation is then applied to the next call on setCallback(), adjusting the requested delay up or down depending on the sign and magnitude of the deviation. If the cumulative deviation is positive (delays are running longer than requested), future delays will be reduced by some amount; if the deviation is negative (delays are running too short), future delays will be increased by some amount. The magnitude of the cumulative deviation is also reduced by these adjustments, with the goal of driving it toward zero.

Thus, instead of trying to make the individual callback delays accurate, the goal of this mechanism is to have the deviations offset each other to produce delays that are accurate in the average.

This mechanism was originally developed for the retro-B5500 emulator, and has been ported more or less as is to the other emulator projects. The portion that adjusts requested delays has required a lot of tuning to make it stable and reduce jitter, however, and that has been an on-going effort in all of the emulators. Last year, the retro-205 emulator exposed an instability in the adjustment algorithm, and I ended up completely redesigning it for that emulator. This release of retro-B5500 now includes that redesign.

Sending a message to yourself always seemed to me to be a bit of a hokey way to generate a yield, and there are potential cross-site security issues with postMessage(). It worked well enough, though, and it was the only approach I had been able to find. Then, while working on the tape drive implementation for the retro-220 emulator last Fall, I started using a relatively new mechanism for handling asynchronous function calls, Promises. Basically, a Promise is an object to which you can attach one or more callback functions. At some point, the Promise is "resolved" (i.e., the thing that was "promised" is reported as being fulfilled) by calling one of the Promise's methods. The Promise then calls each of the attached callbacks, one at a time, in the order they were attached.

This may not seem like much, but Promises turn out to be a very useful abstraction in asynchronous programming. They have two features that really caught my attention:
  1. After attaching a callback function to a Promise, the function will not be called until after the attaching code has returned to the Javascript run-time dispatcher.
  2. It is possible to create a Promise that is in resolved immediately upon creation. Any callbacks that are attached to a resolved Promise will still be called, but only after the code exits back to the Javascript dispatcher.
The fact that a Promise's callbacks are only called after returning to the Javascript run-time means that any other queued callback functions would be called first. That in turn means that a Promise should be able to generate a minimum-delay yield. Some experiments proved this to be true, and apparently with lower overhead and fewer security implications than postMessage()/onMessage. Thus, the setCallback() implementation in this retro-B5500 release also incorporates the new Promise-based mechanism from the retro-220 project.

Minor Refinements

  • Rotational latencies for the Head-per-Track disk units are now computed from the high-precision performance.now() timer instead of the random number generator used previously.
  • The emulator's internal bindMethod() function, used to apply an object context to functions, has been replaced by the standard Javascript Function.bind() method.
  • The emulator's custom utility routines for manipulating a DOM element's className attribute have been replaced by standard DOM methods for the attribute's classList object.

Corrections and Bug Fixes

Two Nasty Bugs

The retro-B5500 emulator has always had a problem running a heavy mix of jobs. We first noticed this when we tried to recompile the Mark XIII system software, running several large compilations at once. The symptoms were Flag Bit and Invalid Address errors. Usually these errors aborted jobs in the mix. The Invalid Address error would occasionally result in an MCP "punt," typing "INVALD ADRSS" on the SPO and halting the system. The errors occurred at random times and in random locations in random programs.

Since this problem seemed to exhibit itself only during a heavy mix, and a heavy mix usually implies significant memory segment overlay activity due to Presence Bit interrupts, it seemed likely that the errors were somehow related to memory overlay or the handling of Presence Bit interrupts. I had tried several times over the past few years to trap these errors as they occurred and to isolate their cause, but without success.

Since I had to dig into the emulator to fix the Safari pop-up window problem anyway, I decided to make yet another run at resolving this long-standing problem. In previous attempts, I had tried to trigger memory dumps that captured the state of the system, but had been unable to generate one that revealed anything useful. Determining the source of the Flag Bit errors proved to be especially elusive.

The Flag Bit is the high-order bit in a B5500 word. Flag Bits mean nothing special in Character Mode, but in Word Mode they distinguish data words (operands) from control words. If the Flag Bit is a 1, it's a control word; if 0, it's a data word. The Processor generates a Flag Bit Interrupt in Word Mode when it fetches a word from memory that has a Flag Bit different than what it is expecting. This occurs in two cases:
  1. In the Operand Call (OPDC) instruction, if an indexed data descriptor references a non-operand word, i.e., one with its Flag Bit set.
  2. When exiting a procedure, if the Return Control Word addressed by the F register has its Flag Bit reset.
Interrupts for condition #1 are definitely errors. In researching this, however, I learned something about the B5500 architecture that I hadn't realized before -- interrupts for condition #2 are most often both intentional and beneficial.

Virtual memory on the B5500 (there's actually nothing virtual about it, but that is a subject for another post) is implemented using control words. Data areas are addressed through data descriptors, and code is addressed through program descriptors, also known as Program Control Words (PCWs). Data descriptors contain the physical memory address of the start of a data area, and PCWs contain the physical memory address of a procedure entry point or a cross-segment branch destination. Both words also contain a Presence Bit that indicates whether the associated data or code segment is physically present in memory or not.

Attempting to access memory through a descriptor having a zero Presence Bit generates a Presence Bit interrupt, which is the B5500 equivalent of a page fault. The MCP reacts to this interrupt by allocating space in memory (overlaying other areas if necessary to make room), making the referenced data or code present in memory, fixing up the descriptor with the physical memory address, setting the descriptor's Presence Bit to 1, and restarting the interrupted instruction. All of this is well known.

There are two other control words, however, that also address code, the Return Control Word (RCW) and Interrupt Return Control Word (IRCW). Both are stored in the stack by the hardware and hold return addresses -- the RCW as a result of a procedure call and the IRCW as a result of an interrupt. Both have a similar format, but neither one has a Presence Bit. So what will happen when a procedure exits back to its caller, but the caller's code segment has been overlaid while the called procedure has been executing?

This seems to be an aspect of the architecture that is less-well understood -- I certainly had not appreciated it before digging into the problem at hand, and have not found any mention of it in the documentation. It turns out that the MCP treats the Flag Bit of an RCW or IRCW somewhat like a Presence Bit. When a code segment is overlaid, the MCP not only resets the Presence Bit in any PCW that references that segment, it also resets the Flag Bit in any active RCW or IRCW that references that segment. If the hardware attempts to return to the address in one of these "absent" RCW or IRCW words, a Flag Bit Interrupt will occur. If the syllable that caused the interrupt is one of the return operators, the MCP will handle the interrupt similar to a Presence Bit Interrupt; otherwise the MCP will DS (abort) the job with a Flag Bit error.

Thus, my first attempts to capture the source of the problem, by trapping every Flag Bit Interrupt, led to somewhat comical results -- a tsunami of memory dumps caused by perfectly normal and legitimate page-fault activity. What I needed was a way to trap the problem at the point where the MCP has determined the Flag Bit Interrupt was not a pseudo Presence Bit Interrupt. I did not want to modify the Mark XIII MCP to do this, so the approach I chose was to modify the emulator to detect entry into the MCP's TERMINALMESSAGE procedure, which generates the program abort message that gets typed on the SPO. TERMINALMESSAGE is a so-called "save" procedure, meaning that it has a fixed memory address and is not overlayable. For the Mark XIII MCP as released, its entry point is physical address 999 decimal.

That approach worked. After running my standard heavy mix batch of jobs a few times and analyzing the resulting Flag Bit dumps, it became apparent that all of the interrupts were occurring at the point a program returned from a Character Mode procedure back into Word Mode.

With that clue in hand, the problem was easy to find -- the emulator was handling both Character and Word Mode exits using the same common routine, but a closer inspection of the Processor Flows and the B5281 Processor Training Manual showed that while Character and Word Mode returns are very similar, they are not the same. The common routine was handling Word Mode returns properly, but not Character Mode returns. In particular, it was not invalidating the A register before checking the Flag Bit in the RCW. Leaving the A register occupied could result an extra word in the stack if the interrupt was triggered, which could throw off the MCP's addressing of the stack while handling the interrupt. Of course, the interrupt was most likely to be triggered during a heavy mix when the probably was high that many code segments would have been rolled out.

Rewriting the procedure return mechanism in the emulator so that Word Mode and Character Mode returns were handled separately completely eliminated the problem with Flag Bit aborts. That left the Invalid Address aborts, which had been occurring a lot less frequently than the Flag Bit aborts, but were still taking place.

Invalid Address Interrupts occur under two conditions:
  1. When the Processor attempts to access a memory location that does not exist. This could be due to a memory module that was not installed or one which had been powered off or switched to local status.
  2. When a Normal State program attempts to access a physical address less than 1000 octal. The first 512 decimal words of memory are reserved for the MCP, and can be accessed only when the Processor is in Control State.
I was running a configuration with a full complement of 32K words, so condition #1 did not apply. In reviewing how the emulator handles condition #2, I realized that the original implementation distributed responsibility for detecting this condition between the Processor and Central Control. In fact, that detection was a matter entirely internal to the Processor. In the process of correcting that, it suddenly struck me that while the emulator was checking for Normal State in the detection of condition #2, it wasn't checking for it in other cases -- an invalid address occurring in Control State should be ignored and not generate an interrupt. It was clear the emulator had been generating interrupts in Control State, and that was the problem, not a bad address.

After fixing that really dumb error, my heavy mix batch now runs successfully and repeatedly. It has taken almost five years to nail these two nasty bugs, but finally it's done.

Miscellaneous Corrections

  1. Disable the word count field in I/O Descriptors for card reader/punch operations. I/Os for both devices did not use this field -- they always access 10 words of memory to transfer 80 characters of data.
  2. Fix a bug when using the RUNOUT button on the card punch to empty the stackers. The code was making an invalid property reference that caused a Javascript error to be thrown.
  3. Implement separate jump mechanisms for Word and Character Mode to correct edge-case errors in branch logic.
  4. Remove extraneous white space from B5500FramePaper.html. This file provides the initial content for the <iframe> elements that represent "paper" in peripheral devices such as the line printer, card punch, and SPO. The extraneous white space produced additional blank lines of text when the contents of the <iframe> were saved to disk or copied to the clipboard.
  5. Correct top-of-form handling in the line printer. Certain combinations of carriage control would interfere with top-of-form handling.
  6. Correct the animation of the tape reel in the tape drive for backward motion.
  7. Make a slight correction to clock counting in Single-Precision Add.
  8. Add P1 S and F register values to the dump caption record in tape images for internal memory dumps to tape (i.e., those generated by clicking the NOT READY lamp on the Console Panel). Also update and improve the dump tape's label records.
  9. Correct the method to "focus" the Console Panel window after the SPO initializes and becomes ready.
The problem resolved in #1 above was causing the Cold- and Cool-Start decks to function improperly. These decks are used to initialize and repair the disk filesystem and to load MCP code files. Both decks consist of the Cold- or Cool-Start program deck, followed by their parameter cards, followed by the MCP Tape-to-Disk loader program deck, followed by its parameter cards.

The Cold- and Cool-Start programs are designed to bootstrap the Tape-to-Disk Loader program automatically when they finish. I had not realized this, and thought you had to halt/load each program individually. The only way I could get that to work was to insert a blank card just before the Tape-to-Disk Loader program.

What was really happening was that the Cold- or Cool-Start program was attempting to read the first card of the loader program into memory, but unlike the MCP, was not setting the word count field in the I/O Descriptor. Thus the card was physically being read, but because the emulator was incorrectly relying on the word-count field, none of the card's data got transferred to memory, so there was no code to execute when the Cold- or Cool-Start program branched to it. Adding the blank card to the deck caused the Cold- or Cool-Start program simply to "eat" that card, allowing a subsequent manual halt/load to boot the loader program starting with its first card.

With this correction in place, that blank card now gets in the way. In this release it has been removed from the COLDSTART-XIII.card and COOLSTART-XIII.card decks available in the tools/ directory of the project repository. Any decks you may have that are based on these two should be checked. Any card between the STOP card that terminates the Cold- or Cool-Start program's parameters and the binary bootstrap card for the Tape-to-Disk Loader should be removed.

Sunday, November 20, 2016

Emulator Release 1.04

Nigel and I are pleased to announce that version 1.04 of the retro-B5500 emulator was released on 4 September 2016. All changes have been posted to the repository for our GitHub project. The hosting site has also been updated with this release, and for those of you running your own web server, a zip file of the release can be downloaded from Google Drive.

This is a minor release containing a number of enhancements and corrections for issues we have discovered since version 1.03 was released a little over a year ago.

Enhancements in Release 1.04

This release of the emulator supports the following enhancements.

New Single-Precision Add/Subtract Implementation

The B5500 uses a unified numeric format, which means that it does not have the typical distinction between integer and floating-point numbers that most computer architectures do. Integers are simply values that have been normalized so that their exponent is zero. For exaple, octal 0000000000000001 (the integer representation of 1) and 1141000000000000 (the normalized floating-point representation of 1.0, i.e., octal 1000000000000 × 8-12) are the same value as far as the B5500 numeric operators are concerned.

There is a document informally referred to as "The Flows," which contains a schematic representation of the logic equations for each operator syllable. Alas, we did not have access to this document when initially developing the emulator, and had to rely on its companion, the Processor Training Manual, which is a good, but not-quite-complete narrative guide to The Flows. As a result, the first implementation of single-precision arithmetic was something of a best-guess effort, and as reported previously in this blog, some of my early numeric benchmarks were producing results that had at best one significant digit of agreement with results from a real B5500.

After much frustrating research, that problem turned out to be improper rounding in single-precision Add and Subtract, which are implemented as the same instruction with some small differences in the treatment of signs. I finally fixed the problem empirically by generating results from 64 "interesting" numeric values and comparing the octal results to those from a modern Unisys MCP system, which uses the same numeric word format. That revealed many cases where rounding was being done improperly -- mostly during scaling of the smaller operand to make exponents equal. Fixing those cases allowed the benchmarks to agree with the results from a real B5500.

By the time I got around to implementing the double-precision arithmetic operators about a year later, The Flows had become available on bitsavers.org, and I followed them closely in implementing the double-precision operators. That gave me the idea that at some point I should go back and redo single-precision Add/Subtract with more rigorous attention to implementing them the way The Flows said they worked.

Earlier this year, I had a lengthy and very interesting exchange of correspondence with Reinhard Meyer in Germany, who was contemplating design of a B5500 emulator of his own and perhaps eventually producing an FPGA implementation of the machine. That exchange finally galvanized me to go back and re-implement single-precision Add/Subtract according to The Flows.

Rigorously following The Flows turned out to be considerably more of a challenge than I had originally anticipated. Digital logic tends to work in parallel, with many states changing at each clock pulse. Most of that translates fairly easily to the sequentially-executed code of a traditional programming language, but some of it does not, and you continuously need to be on guard that states set during one clock cycle do not become effective until the start of the next one.

The most frustrating problem that arose during this effort was one where subtracting two numbers of equal value generated a result that was not completely zero. The mantissa would be zero, but the sign or exponent would not. There is explicit logic in The Flows to test for this condition in the J05L block under the secondary control of Q04F/ (see index 1.01.1 page 2 of 2 in the document) and exit the operator early if the sign or exponent do not need to be cleared to zero. That test is based on the logic term W03L, which is defined as "A[39->37] = B[39->37]", meaning the high-order octade of the A-register mantissa (bits 39 through 37) equals the high-order octade of the B-register mantissa. The corresponding narrative in the Processor Training Manual (page 3.17-11) states, "If the 13th octade of the A and B registers do not equal each other (W03L/) it is not possible for the resultant sum of the complement addition to equal all zeros and the operator is terminated."

I implemented the code exactly that way. It didn't work. After staring at that portion of the logic for days, it finally occurred to me that neither the definition of W03L nor the statement in the Training Manual made sense -- it's entirely possible for the two high-order octades to be unequal and still sum to zero -- 7+1 (ignoring the carry), for example. That made me begin to wonder whether "equal" and "not equal" meant what I thought they did. To answer that question, I had to look at the document upon which The Flows are based, the Processor Equation Book, where on page 422.00, I found the term W03L/ to be defined as:

A39F*B39F + A39F/*B39F/ + A38F*B38F + A38F/*B38F/ + A37F*B37F + A37F/*B37F/

This is somewhat simplified from the notation in the Equation Book, which appears to refer to physical circuit elements. For the uninitiated, "*" means logical-AND, "+" means logical-OR, and "/" used as a suffix means logical-NOT. Operator precedence is NOT-AND-OR, highest to lowest. The numbers refer to bits in the registers, with 48 being high-order and 01 being low-order. A suffix of "F" means flip-flop.

That equation clearly does not determine inequality of the high-order A and B octades -- it determines the logical equivalence of the three pairs of bits in the octades and ORs those equivalences together. Logical equivalence is the complement of exclusive-OR. In other words, W03L/ is true if any of the three pairs of bits match. Equivalently, W03L is true if none of the three pairs of bits match. After making that change in the code, results with zero mantissas finally generated words of all zeroes.

After all the hard work to re-implement single-precision Add/Subtract from The Flows, it produced very little improvement over the results of the previous, empirically-corrected implementation. There were a few differences in the way that results are normalized, and a rounding difference if one of the values is octal 0161000000000000 and the other value is one of 0004444444444444, 0005555555555555, 0006666666666666, or 0007777777777777. On that basis, the effort invested in the new implementation was not worth the very slight improvement in results. On the other hand, it was a very interesting experience, and I learned a lot about digital logic and how the B5500 worked at the electronic level.

There is one other significant difference -- the new implementation runs noticeably slower, in terms of emulated clock time, than the prior one. For one heavily numeric benchmark that runs about ten minutes, the new implementation increases the emulated run time by 48 seconds -- a little over 8%.

Card Reader Enhancement

The implementation of the B129 card reader in the emulator allows you to load one or more text files into the reader as if they are card decks. Each line in a text file is treated as an 80-column card image. Thus, in order to read cards, you first must have a text file on your local system to hold the necessary card images.

This has proven to be a little inconvenient. If you have a file of source code you wish to compile, for example, you either need to modify that file and add the necessary B5500 control cards, or create a copy of the file and add the control cards to that.

Starting with this release, the user interface for the card reader now supports the ability to enter card images directly into the reader from your keyboard without creating a text file first. Card images entered this way can be interspersed with card images from text files. This allows you, among other things, to "wrap" existing files with control cards, compiler $-option cards, or anything else you need but don't have (or don't want) in a text file on disk.

The card reader's panel has a new button in the upper right-hand corner labeled KEY IN DECK. This button is disabled unless the reader is in a not-ready state. Clicking the button opens a sub-window that allows you to enter card images directly into the reader's buffer:

While the key-in window is open, the reader's START button is disabled.

The bulk of this window is occupied by a standard text area. You can enter text into this area from your keyboard or copy/paste text from another source. You can edit the text using the normal controls of your operating system. Clicking the Insert button at the bottom of the window appends the contents of the text area to the card reader's buffer and closes the window. Clicking the Cancel button simply closes the window without affecting the reader's buffer. Once text is inserted into the buffer, it cannot be modified or deleted, just as with inserting a text file into the buffer.

There are three white buttons along the bottom of the window with commonly-used control card images. Clicking one of these simply inserts the respective card image into the text area at the current cursor position. From there it can be appended to the reader's buffer by clicking the Insert button.

Miscellaneous Improvements

1. The size of the terminal adapter buffer in B5500DatacomUnit has been reduced from 112 to 56 characters to accommodate the requirements of the R/C interactive editor. Users of the Timesharing MCP (TSMCP) and CANDE will need to update their SYSTEM/DISK file with the new buffer size, e.g.,
2. We have discovered that the emulator now works with Apple Macintosh OS X using Safari versions 8 and above. Nothing has changed in the emulator to support this -- the issue was waiting for Safari to implement the IndexedDB API used by the emulated disk subsystem.

We continue to be unable to get the emulator to run in either Microsoft Windows Explorer or Edge. If anyone has tried the emulator in Opera or any other GUI browser, please let us know how that works. 

3. Despite a statement in the project wiki that the emulator must be loaded over HTTP from a web server, it appears to run just fine when loaded directly from your local file system, e.g., using a URL like this:
The wiki has been corrected.

4. The alternate B5500SyllableDebugger user interface for the emulator has been enhanced with an Execute Single button. This executes the instruction currently in the T register, but differs from the Step button in that it does not advance the C and L instruction counter registers. This makes Execute Single a better choice than Step for testing and debugging a specific operator.

5. The default height of the SPO and line printer windows has been reduced to about a third of the screen height.

6. The tools/B5500DeleteStorageDB.html script can be used to remove the IndexedDB database for an emulated disk subsystem. Previously, this script could only remove the default data base, named B5500DiskUnit. The script now takes a "?db=" query string parameter with the name of the data base to remove.

7. The Algol Glyphs option for the card punch (CPA) has been set to false by default. Some text editors were corrupting the Unicode characters produced in Cold- and Cool-start decks when this option was set to true, making the decks useless for loading under CARD LOAD SELECT.

8. The exponential moving average computations for Delay Delta and Processor Slack in the Processor object have been adjusted slightly.


1. Rounding in Integer Store operators has been corrected to produce a word of all zeroes if the mantissa is zero. Previously, non-zero sign and exponent fields could have been present when mantissa was scaled to zero.

2. BCL translation for alpha-mode (even parity) tape files has been corrected. Previously, the translation was being done twice -- once by the I/O Control Unit and once again by the tape drive. It is now handled entirely within the tape drive.

3. Both EOF and parity error are now reported by the tape units for any attempt to read beyond the end of a tape image.

4. The way that "green-bar" formatting was being done on the line printer was causing a blank line to be inserted every third line when copying text directly from the "paper" area of the printer's window. A different method is now used that does not result in extraneous lines in the copied text.

5. When the emulator was powered on, the browser windows for the individual peripheral devices were being created twice. This behavior was intentional, but dated from very early in the emulator's development when crashes and aborts were a frequent occurrence. The act of opening a window, closing it, and then reopening it served to close any windows left open by a crashed emulator instance. We are long past the need for that now, so the screwy open/close behavior has been removed. As part of this effort, de-registration of event handlers for the various windows has been implemented.

6. After almost five years, we are still finding errors in the transcription of the Mark XVI Algol compiler listing. Most of the errors that have come to light in the past few years have been in comments, but a few months ago we found a serious error in line 06073850 -- a plus sign had been transcribed as a minus. I doubt we are ever going to get all of the errors out of this transcription -- or of any other large transcription like this.

The B5500 Source Code Archive

We are starting to amass a significant amount of source code for the B5500. Most of this has been recovered by transcribing it from listings. Up to this point, we have been including the transcriptions we've done or received from others in the emulator project's repository. Rich Cornwell in North Carolina (US) and Jim Fehlinger in New Jersey (US) have been especially prolific in turning scanned listings into text files.

There are now multiple B5500 emulators, however, either in existence or under development. In addition to Sid McHarg's C++ emulator and this project, Rich Cornwell completed an emulator earlier this year that runs in the SimH framework, and Mark Lloyd in the UK continues to work on his Lazarus/Pascal version.

With multiple emulator projects and multiple sources of recovered source code, it has becoming less and less appropriate for that code to be embedded in a specific project. I have been thinking about this for a while, and a few months ago decided to set up a separate project on GitHub to serve as a common repository for all B5500 emulators:
Rather than set this up under someone's personal account, I've created a GitHub "organization" to be the owner. This will make it easier to share and ultimately transfer responsibility for the archive, and also to create additional repositories in the future to maintain recovered source code for other systems. Initially, Nigel and I are the administrators of this organization and the B5500 archive, but hopefully that can be expanded in the future.

To seed this new repository, I have exported the source code transcriptions, and their history, from the retro-b5500 project and made that the initial contribution to the B5500-software project. In the process, I rearranged the directory structure into something that hopefully will be more maintainable over time. The source code in the retro-b5500 project will probably be deleted at some point down the road.

In a future blog post, I will propose some standards and procedures for maintaining the archive, but will be happy to receive comments and recommendations on this from others. I will also be happy to receive new contributions and will consider pull requests for updates. Each transcription should be accompanied by some text that describes it provenance and who did the transcription. See the README.txt files in the repository for examples of what I'd like to have in this regard.

I would like for all of the transcriptions to be faithful to their original sources, so please include sequence numbers if they are present in the original, and please do not correct spelling in comments, or even bugs in the code. Any original errors can be corrected in separate projects or branches from the transcriptions of the originals.