Welcome to the Datatron 205 and 220 Blog

This blog is a companion to T J Sawyer's Web Page that outlines the history of the Burroughs Datatron 205 and Paul Kimpel's incredible 205 and 220 emulators. Please visit those sites and return here to post any comments.

Wednesday, August 23, 2017

BAMCO Lives!

When W. S. Burroughs and his three moneyed associates formed a company in St. Louis in 1886 to market Burroughs' invention, they named it the American Arithmometer Company.  This turned out to be a poor choice of name since few people could successfully spell (or even pronounce) "Arithmometer."

In 1905, after relocating to Detroit, the company was renamed as Burroughs Adding Machine Company a name that lasted until 1953 when the name became The Burroughs Corporation.  Burroughs Adding Machine Company was easily and frequently abbreviated to BAMCO internally.  So imagine my surprise upon seeing this T-shirt on a server at McDonalds in southern Minnesota.






Apparently the name is now used by BAMCO, Inc., the local McDonalds franchisee in Mankato.  I don't know if they have battled for the legal rights with other folks like the Bon Appetite Management Company.

Similar name confusion exists with the name Datatron which now is usually associated with a game in the form of a card-based game, Morphtronic Datatron.

Likewise, ElectroData searches will usually lead to the Electrodata company a manufacturer of communications testing equipment.



Monday, July 31, 2017

Research Is Hard Work - and it Takes a Lot of Luck

When I put together my Datatron Maintenance Page on the Burroughs 205/Datatron site, I mentioned Bill Oller, the Pacific Power & Light maintenance engineer.  Bill gave me my first "computer job" in the fall of 1963 - testing a couple of hundred tubes that he had ordered for his preventive maintenance routine on the University of Portland 205.

I wished I had a picture of Bill to include on that page and spent a good deal of time trying to locate Bill or his family all to no avail.  (It turns out that Bill passed away in 2002 in the Coeur dAlene Idaho area).  I wasn't even sure of the correct spelling of his last name.  But as luck would have it, today I came across a picture of Bill and the PP&L tape subsystem.

I was browsing The Oregonian website for some family history information.  The Oregonian does not participate in the major newspaper archive sites that are so easily searched and show up in response to Google searches; they have their own archive with a rather clumsy interface and sell access by the day for low volume users such as I.  After finding my relative's obituary, I did some computer history searches looking for the words "computer" and "bank."  This turned up some useful history of a pioneering U.S. Bank project to automate account posting.

From January of 1957 in The Oregonian:


Announcement that the U. S. National was making use of the electronic computer on an ex­perimental basis in connection with the operations of its Sheri­dan branch was made in The Oregonian last November. At that time it was believed to he the first bank in the world to start posting its checking ac­counts with an electronic brain.
John N. Raleigh of the bank's "methods" department had developed a branch posting system on an IBM 650 - he called it SONIA. (System of Numeric Integrated Accounting, likely inspired by BofA's ERMA acronym)  SONIA was good for the bank and very good for Raleigh - he was later to become president of the San Fernando Valley Bank.

As I later read a 1960 article that also mentioned the bank system, my eye was drawn to two accompanying photographs.  My eye naturally went first to the five young ladies in bathing suits who were marveling at the the IBM RAMAC disk drive.   Have you noticed that the 1950s computers always featured an attractive damsel at the console?  Jantzen, the Portland swimsuit manufacturer, went them one - no, five better!  Jantzen's was most likely the most popular booth at the Business Machine Show.



But there, just below the ladies and the RAMAC, stands Bill Oller with his hands on a Datatron pluggable unit in the Tape Controller.  I now have my picture for the maintenance page.

The Jantzen picture will doubtless reappear on a disc storage page on the site in due time.

 

Saturday, April 1, 2017

Emulator Version 1.00 and the DataFile

Development of the retro-205 emulator for the ElectroData/Burroughs Datatron 205 computer system is now functionally complete. That is not to say that it is finished. As a friend used to say, programs are never finished -- they merely become obsolete.

This is Paul Kimpel with another guest post on Tom's 205 and 220 blog. It is time to drag the emulator out of its 0.xx development era, so I am pleased to announce that the version just released has been labeled 1.00. Rather than have one monster post that describes all of the changes in this release, this post will discuss the implementation of the final piece of the 205 hardware, a magnetic tape device known as the DataFile. It will also describe a few bugs and enhancements that have been addressed. A second post will discuss the other major improvement in this release -- emulator timing.

The DataFile


The Datatron 205 supported a relatively typical, reel-to-reel magnetic tape drive, known as the Model 544 DataReader. To understand the DataFile, let us start with a description of the DataReader.

Overview of the DataReader

The DataReader was connected to the 205 processor through the Model 543 Tape Control Unit. It used 2500-foot reels of 3/4-inch magnetic tape. The tape was recorded in two parallel lanes of six tracks each. Four tracks were used to record BCD numeric data, the fifth track was even parity, and the sixth track signaled the start of a block. Blocks were a fixed 20 words each, matching the size of the high-speed loops on the 205's memory drum. The tape moved at 60 inches/second, with data recorded at 100 bits/inch. Total length of a block, including inter-block gap and a block header, was 2.78 inches. Total capacity of a reel of tape was 10,000 blocks per lane.

Blocks in a lane were numbered from 0 through 9999. Individual blocks could be read and written, and since they were of fixed length, and even reliably overwritten -- something that could not be done with the IBM recording format that later became the de facto standard. The 205 processor had a tape search instruction that specified the lane and block number sought. Once initiated, the Tape Control Unit conducted the search either forwards or backwards, asynchronously and independently of the processor.

This was in the pre-disk era of computing, and much data processing was done using sequential file techniques. The asynchronous search, dual lane, and overwrite features gave the 205 some interesting advantages in applications that had to maintain large data sets on magnetic tape. A number of clever indexing and lane-switching techniques were developed to take advantage of them, some of which are described in Bulletin 3024, The Magnetic Tape System Handbook.

Even with these advantages, tape operations were still very slow. Average time for the DataReader to locate a random block on a full reel of tape was almost four minutes. Disk technology was on the horizon, but very expensive -- IBM was to introduce its 305 RAMAC system in September 1956, shortly after Burroughs acquired ElectroData. The RAMAC disk boasted an access time of 600ms, but a total capacity of only five million digits, hardly more than the 4.4 million digits on a single reel of 205 tape.

The DataFile Compromise

Into this situation, ca. 1954, stepped Duncan MacDonald, one of the lead designers at ElectroData. Realizing that the search would be faster on a shorter tape, and that data capacity was proportional to the total length of tape, he came up with a mechanism that used shorter tapes, but lots of them. The result was the Model 560 DataFile, for which MacDonald was awarded U.S. Patent 2,914,752 in 1959.

The DataFile was housed in a long, waist-high cabinet with a hinged cover, leading to the enclosure being nicknamed "the coffin." This image from Tom Sawyer's web site (and originally from the Charles Babbage Institute) shows the unit with its cover open:
Model 560 DataFile Unit
Inside the enclosure were a set of horizontal rods, over which were draped 50 tapes. Underneath the rod assembly was a moving carriage with pinch rollers and two tape heads. The carriage could traverse under the tapes and stop at any one of them, latching into position and allowing the pinch rollers to engage with a rotating capstan to drive that tape forward or backward. The time to traverse and latch into position varied linearly from about 0.5 to 2.0 seconds, depending on the distance traversed. Thus the carriage could relatively quickly select any one of the 50 tapes and read or write it.

Below the rods the cabinet was partitioned into 50 parallel bins, each wide enough for a tape, with perforated walls separating the tapes. The tapes fell loosely into these bins -- they were not wound on reels or hubs. The perforated bin walls allowed air to enter and exit the bins as the carriage drove a tape back and forth, causing the tape strip to rise or fall within the bin.

The tapes, tape heads, and recording format used with the DataFile were exactly the same as the reel-to-reel DataReader, except that the individual tapes were 250 feet long instead of 2500 feet. Each tape still had two lanes, but only 1000 blocks per lane. Tape speed, recording density, block size, and the overwrite and asynchronous search features were all the same as for the DataReader.

The DataFile used the same Tape Control Unit, which could support any combination of up to ten DataReaders and DataFiles per 205 system. One DataFile could store 50×2×1000 = 100,000 blocks or 22,000,000 digits, and a fully-configured system up to 220,000,000 digits. This was mass storage, ca. 1955.

Unlike the DataReader, the individual tapes in the DataFile were not removable, except for servicing and replacement when they became worn. Thus, the DataFile acted much more like a disk unit than a tape drive, albeit a disk unit with a rather severe latency problem. Burroughs quoted the average time to access a random block within a tape as 15.3 seconds, and an average of 16.3 seconds if the carriage had to move. This was a lot better than four minutes for the DataReader, but a lot worse than 600ms for the RAMAC, once it came along. The DataFile was thus a compromise design, filling a niche in the time between tape- and disk-based eras of data processing.

As you might expect of a device with a heavy carriage that (a) moved quickly when traversing, and (b) was surrounded by flimsy tapes, the DataFile could be high-maintenance, to the point where its less-charitable nickname was "the DataFail." In addition to its mechanical complexity, the tapes were subject to wear, and if left motionless in their bins too long, could settle and crease, causing data errors. Standard operating procedure called for all tapes to be moved from end-to-end at least weekly to avoid this, a process that could be done off-line from the unit's control panel.

Programming for the DataFile was similar to that for a DataReader. The only significant differences were:
  • A DataFile unit had 100 lanes instead of two.
  • Individual lanes held one-tenth the number of blocks compared to the DataReader.
  • The DataFile did not respond to tape rewind commands. To position a tape to its beginning, you did a tape search for block 0000 on an appropriate lane.
The tape instruction formats for the DataFile were the same as for the DataReader, with one exception in the way lanes were selected by a Magnetic Tape Search (MTS, 42) operation. The format of that instruction was:

s hhup 42 aaaa

where s is the sign digit, hh specifies the lane number, u is the tape drive unit number, p is the standard breakpoint digit, and aaaa is the address field, which for this instruction specified the block number to which the tape should be moved. If the low-order bit of the sign digit was set, normal B-register modification would occur, and the effective block number would be the sum of the contents of the B register and the address field.

When this instruction was used with a DataReader, only the low-order bit of the hh field was significant, as there were only two lanes to select between. When used with the DataFile, the full two-digit lane number (00-99) was significant.

The retro-205 DataFile Implementation

Even though the recording and instruction formats for the DataFile were the same as the DataReader, implementing the DataFile in the emulator posed some issues. The major one was persistence of the data. Since the tapes are not removable, their data cannot be stored outside the emulator in ordinary files, the way that tape image files used with the DataReader are. The emulator needed to store the tape data internally in a way that would survive across emulator sessions and workstation restarts.

The solution was to accept that the DataFile is more like a disk unit, and to apply the same approach I used to implement Head-per-Track disk units for the retro-B5500 emulator. Modern web browsers support a lightweight data base API known as IndexedDB. This is not a relational data base, but instead stores Javascript objects. The objects are stored in one or more structures termed object stores, which are similar in concept to tables in a relational data base. The objects within a store may be indexed by properties of the objects themselves, or by external values supplied by the application.

To support the DataFile, the retro-205 emulator uses a single IndexedDB data base, named "D205DataFileDB". This data base is created automatically the first time that a DataFile is added to the system configuration. Each "physical" DataFile unit, lettered A through J in the configuration, has a separate object store in this data base. That store is permanently associated with that physical device letter. If you reconfigure that device letter to be a DataReader, the store will continue to exist, but be hidden. If you later configure the device letter back to a DataFile, the data in the store will again be available. Note that the unit designation numbers (1 to 0 [for unit 10]), selected on the drive panels and used by the software to address tape units, can be changed at any time without affecting the object stores.

Within each object store for a DataFile unit, the data is stored as objects that are 20-word arrays. Each object corresponds to one block on one of the tape strips for the unit. These objects are indexed by a number computed as (lane number)×1000 + (block number), where the lane number ranges from 0-99 and the block number from 0-999 within a lane. While internally the block objects can be accessed quickly and randomly, the emulator executes the necessary delays to model the timing of the DataFile device.

Once the IndexedDB data base is created, it resides permanently on your workstation's local disk. The emulator does not provide a way to remove it, although most browsers allow you to inspect and remove these data bases through their "developer tools" interface. If you remove the data base for the DataFile, it will simply be recreated again the next time you run the emulator with a DataFile present in its configuration. Any former data for the data base will have been lost, of course.

Different browsers implement IndexedDB data bases in different ways. For example, Firefox uses SQLite as the underlying storage engine, while Google Chrome uses LevelDB, a derivative of its BigTable technology. Therefore, you will not be able to share the data base for a DataFile between browsers.

In addition, these data bases are subject to the browser's "same-origin" policy, meaning that resources associated with one web site generally cannot be accessed by resources from some other web site. The combination of scheme (http/https), host name, and port in a site's URL is used in determining same-origin access. For example, you can access the emulator's hosting site as http://www.phkimpel.us/ElectroData-205/ or as http://phkimpel.us/ElectroData-205/ -- both URLs refer to the same physical files on the same physical web server, but to your browser they are different origins, and consequently will use different IndexedDB instances.

Using the retro-205 DataFile

As with all peripheral devices configured in the emulator, each DataFile unit will display a separate window on your screen, similar to this:

retro-205 DataFile Device Window

The controls on this window are similar to those for the DataReader. The area below the controls shows a representation of the 50 tape bins. The brown bars show the relative position of the each tape, with a zero-length bar representing the tape in a rewound (block number 0000) position. Below the bins is a representation of the carriage and tape heads, which move in response to lane selection by tape search instructions. The currently-selected bin is highlighted in yellow.

When the emulator initializes, each DataFile in the configuration is opened with its tapes in random positions. The carriage is initially positioned at tape 25, lane 50.

The DESIGNATE pull-down list selects the unit number that will be recognized by magnetic tape instructions. The REMOTE/LOCAL and NOT WRITE/WRITE switches function the same as for a DataReader. The unit must be in Remote (ready) status in order to be addressed by software. Setting the NOT WRITE switch prevents writing to any of the tapes, but the tapes will still move in response to write instructions. A real DataFile had a set of individual write lock-out tabs for each tape, but this has not yet been implemented in the emulator.

The REWIND and STOP REWIND buttons are always active, regardless of the setting of the REMOTE/LOCAL switch. Clicking REWIND will initiate a rewind of all tape strips, in the manner it worked on a real DataFile. The carriage will move to the left-most tape (for lanes 0/1) and rewind it. Then it will move to the right-most tape (for lanes 98/99), rewinding each tape in reverse sequence until it again reaches the left-most tape. Clicking STOP REWIND will interrupt the process and return the carriage to the left-most tape.

A full rewind on a real DataFile would take an average of more than 20 minutes, depending on the positions of the tape strips. In implementing the rewind function in the emulator, reproducing that particular timing behavior of a real system did not seem necessary, so the emulator presently rewinds DataFile tapes at ten times the speed of a real unit -- averaging a little over two minutes to complete.

Applications for the DataFile

The DataFile is useful for storing large programs and any data that you want to persist across emulator sessions. The Shell Assembler was specifically designed to work with the DataFile, and Donald Knuth's Algol-58 compiler works well from it, too. One issue is that there is no built-in way to load data into a DataFile -- you must write a program to do that.

Here is a small card-load program that will load the Shell Assembler to lane 89 on a DataFile designated as unit 0 (i.e., 10) from lane 1 of a DataReader designated as unit 1:
https://github.com/pkimpel/retro-205/blob/master/software/Shell-Assembler/Shell-Utilities/Shell-Load-DataFile.card
The tape image used by this program can be downloaded from:
https://github.com/pkimpel/retro-205/blob/master/software/Shell-Assembler/Shell-Assembler-Load.tape
Once loaded to the DataFile, the assembler can be executed using this small card-load program:
https://github.com/pkimpel/retro-205/blob/master/software/Shell-Assembler/Shell-Assembler-Bootstrap.card


Bug Fixes and Minor Enhancements


The 205 floating-point implementation has a nice trick -- you can discard the fractional part of a number by adding the floating-point value 5800000000 (i.e., 0.0 × 108) to it. Algebraically this is meaningless, but the way that floating-point addition was mechanized caused the augend value in the A register to be scaled (shifted right) during the alignment of exponents that took place before the addition. Any fractional digits in the value would shift off the low-order end of the number and be lost. The normalization (shifting left) that occurs after the addition would then put the remaining digits back in their original positions. Alas, this was not working in the emulator. The problem was that I was normalizing the addend value in the D register before scaling the A register. By reversing the order of scaling and normalization, that trick now works properly.

Tom Sawyer discovered that the tape drives were not properly transferring data when the operand address was not aligned on a mod-20 boundary. The problem had to do with the way that tape reads and writes are buffered in the 4000 and 5000 high-speed loops on the memory drum. The word at the mod-20 memory address was being mapped to the first word in the tape block, rather than the word at the operand address being first in the tape block. This has been corrected.

Tom also discovered that the way the emulator was interpreting the breakpoint digit in instruction words was not correct.
  • The low-order three bits of that digit are a mask. If any of the 1-bits in that mask match the setting of the breakpoint switch on the Control Console, the processor will halt with a BKPT alarm after the execution of that instruction.
  • The high-order bit in the breakpoint digit is used with the SKIP switch. If that switch is on and the high-order bit of the breakpoint digit is a 1, the processor will skip that instruction. It does so by remaining in the fetch state rather than switching to the execute state, allowing the next instruction to be fetched immediately.
The problem occurred when both the breakpoint and skip conditions were true for the same instruction. In this case, the instruction should not be executed and the processor should stop at the end of the fetch cycle for that instruction. Instead, it was doing this only if the low-order bit of the breakpoint digit was a 1, not if the breakpoint switch matched one of the 1-bits in the breakpoint mask. This has also been corrected.

One of the items in the material Prof. Knuth sent me last year was ElectroData Technical Newsletter #5, dated February 14, 1958. This document described how all 100 two-digit character codes mapped to glyphs on the Flexowriter and Cardatron output devices. Character translation in the emulator for these devices has been changed to match the information in that document.

Input translation from ASCII to internal 205 code for card devices has been altered slightly in order to improve support for Cardatron reload lockout. In the Cardatron, reload lockout could be imposed by a multi-punch combination in the column that selected the format band. A numeric punch (1-7) selected the format band as usual; an 8-punch signaled reload lockout. Trying to simulate this with ASCII character codes is a little awkward, but here is the current arrangement:
  • the grave accent (`), a 1-8 punch, now maps to the digit 1;
  • the colon (:), a 2-8 punch, now maps to the digit 2;
  • the "#" is a 3-8 punch;
  • the "@" is a 4-8 punch;
  • the characters "|", "}", "~" now arbitrarily map to the digits 5, 6, 7, representing 5-8, 6-8, and 7-8 punches, respectively ;
  • the double-quote ("), a 7-8 punch, now maps to the digit 7;
  • the apostrophe (') now maps to the "@" but is treated as a 5-8 punch for reload lockout.
On the Flexowriter, clicking the RESET button on the Typewriter Control Unit panel will now output a new-line sequence if the carriage is not at the left margin. Previously, there was no way to reset the carriage if the computer stopped in the middle of a line.

Please stay tuned for the second post on improvements to emulator instruction timing.

Tuesday, February 28, 2017

Gloria Bullock

This is Tom Sawyer again, noting a new page added to my Burroughs 205 history website.  There are many interesting people and personalities associated with the Datatron and one that has long interested me is Gloria Bullock.  Gloria shows up early in the organization - even before there was an ElectroData.  She is found in the Mathematics section of the fledgling organization, but there are references to her being involved with training too.

When I came across a 1959 Los Angeles Times story noting an Urban League award presented to Bullock by baseball star Roy Campenella, I knew this was someone I wanted to learn more about.  I was a big "Campy" fan.  The California Eagle characterized the award as being "for her part in perfecting computing machinery."

Last summer, I finally came across an internal Burroughs publication from 1957 that designated Bullock as the first Datatron programmer.  With the recent publicity surrounding the movie, "Hidden Figures," I thought it might be time to put together Gloria Bullock's chapter in the Datatron story.  (The book is much better than the movie, in my opinion.) The result is this page on the 205 history website.

My one regret is that I have not been able to close out the story beyond about 1977 when Gloria was living in Palo Alto near her younger sister in Moss Beach.  Perhaps, as is sometimes the case, a mention on the Internet might turn up someone who knows the answer.

Revisions to the Blog Format

This is Tom Sawyer, posting to the Datatron blog after a long absence.

This blog has been very difficult to read from the beginning but the problem became terribly apparent after Paul Kimpel's lengthy posts about the 205emulator.  I finally bit the bullet and made revisions to the layout a few days ago and believe we now have a much more readable blog.

I will attempt to make more frequent contributions of a historic nature as I continue to add to my ElectroData / Burroughs history website and I am certain that Paul will be keeping us up to date as he moves forward with his next project, the Burroughs 220 emulator.

Saturday, December 17, 2016

Emulator Configuration and Version 0.06

It has been more than a year since an update to the retro-205 emulator was last released. I have been tinkering with a few things inside the emulator over the past year, but most of the work during that time has been devoted to reconstructing software for the 205, particularly Donald Knuth's Algol-58 compiler and the Shell Assembler. Both of these have been discussed in prior posts.

This is Paul Kimpel, with another post on Tom Sawyer's 205 blog. Last month I got back to some serious development for the emulator. With that effort now complete, it's time to push out a formal 0.06 release and describe what has changed since 0.05 was released in August 2015. This post will discuss those changes and further project activity in the following main sections:
  1. System Configuration Facility
  2. Cardatron Leading-Zero Suppression
  3. Control Panel Display Enhancements
  4. Other Corrections and Enhancements
  5. Future Work

System Configuration Facility


Like the color of Henry Ford's Model-T automobile, up until now you could have had any configuration of peripheral devices for the retro-205 emulator you wanted, as long as it was the one I had hard-wired into the software. The I/O architecture of the emulator was designed from the beginning to support user-specified configurations, but I had yet to implement the user interface and persistence mechanism necessary to realize of that part of the design. I finally completed that late in November.

The emulator home page now has a second button below the picture of the 205 at Detroit Arsenal, labeled Configure System.

Emulator home page with new Configure System button

Clicking that second button opens a sub-window, which shows the current system configuration and allows you to change it. The button is enabled -- and the configuration can be accessed -- only after the emulator is initially loaded or when it is in a powered-down state.

Sample emulator configuration window

This page has three areas for selection of Console Units, Cardatron Units, and Magnetic Tape Units. You can change the configuration by altering the controls in this window and clicking the SAVE button. If you only want to view the configuration, or to discard any changes you have made, click CANCEL. Both buttons close the window.

Persisting the Configuration


The configuration data is saved using an HTML5 feature known as Local Storage, implemented in Javascript by the window.localStorage object. Local Storage is supported by reasonably recent versions of all the major browsers. It allows a web page to store information that will persist across browser sessions and workstation restarts. For example, Firefox stores this data in a SQLite data base on your local disk drive.

Data in Local Storage is subject to the browser's same-origin policy, meaning that separate copies of the data are maintained for each combination in a URL of scheme (http/https), host name, and port number. Thus, if you are accessing the emulator on two different web sites, you will have a different copy of the configuration data for each one. The host name is taken literally -- you can access the emulator on my hosting site either at http://www.phkimpel.us/ElectroData-205/ or at http://phkimpel.us/ElectroData-205/. Both URLs will access the same physical files on the server, but those are different origins as far as the browser is concerned. In addition, Local Storage data is maintained differently by different browsers, so Firefox and Google Chrome, for example, will have separate copies of Local Storage data as well.

When you first run retro-205 version 0.06 in your browser, the emulator will recognize that no configuration data is present, and will automatically create a default configuration for you. That configuration is the same as the one that has been hard-wired into the software up to now:
  • Console devices: Flexowriter printer, paper tape reader, paper tape punch.
  • Cardatron devices: Card Reader 1, Card Punches 1 and 2, Line Printer 3.
  • Magnetic tape devices: DataReaders A, B, C, designated as units 1, 4, 5, respectively.
The emulator has previously used Local Storage to persist switch settings for the Supervisory Panel, Control Console, and Flexowriter/Typewriter Control Unit. Those switch settings are now maintained in the new configuration data. When the 0.06 and later emulators first create the new configuration data, they will look for the older switch settings and apply those to the new configuration. The older switch settings will then be deleted. If you subsequently fall back to an older version of the emulator, the older switch settings will no longer be there and will revert to default values. The new configuration data will not be affected by such a fall-back however.

The following sections discuss the areas of the System Configuration window and how you use them to specify the set of peripherals to be included in your 205 system.

Control Console Units


The Control Console is the easiest section to configure. There are three checkboxes to specify whether you want the Flexowriter, paper-tape reader, and paper-tape punch. Any combination of these may be included or excluded from the configuration. The decimal-only keyboard is always present in the configuration, and thus is not represented in the configuration window.

The 205 had two physical paper-tape readers, the slow-speed mechanical reader that was part of the Flexowriter, and a high-speed optical reader. Both are implemented as one unit in the emulator, with the INPUT knob on the Control Console selecting the speed at which the device will operate. Similarly, the 205 had two paper-tape punches, one part of the Flexowriter (punch output was a by-product of printing), and a separate high-speed unit. The emulator supports only the high-speed punch.

Note that if you exclude one of the Console units from the configuration and then attempt to address that unit with a console I/O instruction, the processor will hang on the I/O, waiting for the device to respond. Generally, the only way out of this situation is to clear the system and start over. This is apparently the way a real 205 behaved as well.

Cardatron Units


The Cardatron supports card readers for input, plus card punches and tabulators (line printers) for output. The Cardatron Control Unit had seven slots into which input or output units could be plugged. By convention, input units were numbered from one starting at the left-most slot; output units were numbered from one starting at the right-most slot. Therefore, input unit #2 would occupy the same slot as output unit #6, although of course only one type of unit could be plugged into a slot at a time.

The System Configuration window uses the same convention. For each slot, you can choose a card reader, card punch, or line printer, or you can leave the slot unused. To the right of the unit selection lists are two sets of checkboxes, which apply only to output units:
  • If Algol Glyphs is checked, the "&#%¤" characters will print as "+=()", respectively. If this box is unchecked, those characters will print as is. This option can be changed on the device window.
  • If Greenbar is checked, output to a line printer device will be grouped in lines of three, with the background color for each group alternating between green and white, simulating the appearance of the pin-feed paper stock typically used with such devices. This option can also be changed on the device window, but is ignored by card punch devices.
Card reader device windows have pull-down lists to specify how their format band is to be selected and the card column from which the band number is to be sensed. These cannot be set on the configuration window, but their setting is now recorded in the configuration data and persisted across emulator sessions.

Card punches and line printers have a new capability with this release to perform leading-zero suppression in specified fields of the output card or line image. The fields are specified as a list of one-relative column numbers in which zero suppression is to begin. The current list of column numbers is shown on the configuration window, but can be set only on the device window. The list of columns is ignored for card reader devices. See the Cardatron Leading-Zero Suppression section below for more information on this feature.

Magnetic Tape Units


The 205 supported two types of magnetic tape devices, the Model 544 DataReader (a traditional reel-to-reel unit) and the Model 560 DataFile (a semi-random access device that used a movable read/write head to access 50 tape strips stored in parallel bins). The emulator currently supports the DataReader, and support for the DataFile is in progress (see below).

Magnetic tape units are identified in two ways. Each of the ten possible units is assigned a letter (A-J) that defines its physical connection to the system. In addition, the drives each have a ten-position switch that specifies their "unit designate." This switch was set by the computer operator. Tape instructions in the Processor addressed units by their designate number, 1-9 plus 0 (for 10), not by their physical unit designation.

The System Configuration window allows you to specify the type of unit for each of the ten physical connections to the system. Until the DataFile is implemented in the emulator, that selection will be treated the same as "(not used)."

You also specify the initial unit designate for each of the devices on the configuration window. This can be changed on the individual units once the emulator has initialized and the unit windows are displayed. Changes made on the unit windows will now be recorded in the configuration data and persisted across browser sessions.

Each unit also has checkboxes on the configuration window to specify the initial state of three switches on the individual unit windows:
  • Remote: If this switch is on and a tape is loaded in the drive, the unit will be in a ready condition and can be addressed by the Processor. The switch must be in Local in order to load or unload tapes, or to manually rewind the tape.
  • Rewind-Ready: If this switch is on and Remote is also on when a tape finishes rewinding, the unit will be in a ready condition. This setting will be ignored by DataFile units, which did not support programmatic rewind.
  • Not-Write: if this switch is on, the unit is inhibited from writing to the tape.
As with the unit designate setting, changes to these switches on the unit windows are now recorded in the configuration data and persisted across emulator sessions.

Note that the contents of tape reels are not persisted across browser sessions. All units are implicitly unloaded after the emulator initializes. When you shut down the emulator by pressing the OFF button on the Supervisory Panel, the contents of all mounted reels of tape are lost. To save data that has been written to tape, you must manually unload the tape and save its contents.


Cardatron Leading-Zero Suppression


The traditional notation we use for decimal numbers presents only the significant digits of the number. Leading zeroes in the whole (integer) portion of a number are not normally written, and often trailing zeroes in the fractional portion of a number are not written as well. Because people are so used to this convention, and because suppression of non-significant zeroes makes the magnitude of a number easier to grasp, computer systems have long supported hardware and software facilities to suppress these non-significant zeroes.

On the 205, leading zero suppression for output devices was done by the devices themselves, not by the Processor. There is a Zero Suppress switch on the Console's Typewriter Control Unit that will suppress leading zeros in numeric values by translating them to spaces before sending them to the Flexowriter.

For Cardatron output devices, leading-zero suppression was done by means of plugboard wiring on the IBM card device -- a Type 407 tabulator for printing or a Type 523 summary punch for cards. As described in the blog post for Knuth's Algol-58 compiler, the behavior of zero suppression on these IBM machines worked in a non-obvious way. Pins on the IBM machine's plugboard identified columns on the output record where zero suppression was to begin. Suppression continued in the columns to the immediate right until a character was encountered that has a "numeric" punch in its card code. At that point, further zero suppression was discontinued until the next column identified for zero suppression, if any, was encountered.

The curious part of this arrangement was the definition of a numeric punch. An IBM card consisted of 80 columns and 12 rows. The top three rows (designated as 12 [+], 11[-], and 0) were termed the "zone" punches. The bottom three rows (designated 1 through 9) were termed the "numeric" punches. Thus, 0 was considered to be a zone, not a numeric punch. Many characters in the IBM card code were represented by two or more punches -- the letter "D" is represented by a 12-4 punch and "$" by an 11-3-8 punch, for example. If a column held a character that has a numeric punch in its card code, that character would discontinue zero suppression.

There are only four characters in the IBM card code used with the Type 407 and 523 devices that do not have numeric punches: "&" or "+" (12 punch), "-" (11 punch), "0" (0 punch), and blank (no punches). Thus as long as columns in a zero-suppression sequence had one of these character codes, suppression would continue, and those columns would be changed to blanks. Of course the way you generate blanks on a card or line of print is to punch or print nothing at all, so zero suppression was effectively output suppression for those columns.

The emulator does not attempt to emulate the IBM devices themselves. Attempting to emulate all the things you could do with a 407 plugboard would be an interesting, but quite daunting task. Thus, the emulator has not been capable of zero suppression on Cardatron output. For example, line 938 from the Algol-58 listing (on page 17):

         313 1 0003 12 0000¤             1   3000000 STA   OUT        D1

is actually output by the MEASY assembler like this:

   0000 0313 1 0003 12 0000¤     0       10003000000 STA   OUT   0  0 D1
   ^    ^                        ^       ^^                      ^  ^
....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|

        10        20        30        40        50        60        70
The ruler below the line shows the 1-relative column positions on the line. In his documentation for the MEASY assembler, Knuth specified the "Break Zero Print Control" columns for the 407 to be 4, 9, 34, 42, 43, 66, and 69, as denoted by the "^" characters above the ruler. This causes columns in that line to be blanked as follows:

   0000 0313 1 0003 12 0000¤     0       10003000000 STA   OUT   0  0 D1

In this case, only zeroes are being suppressed, as you would expect. The reason this was an issue with the Algol-58 compiler recovery was lines like this:

   0000 0002 0 0000 15 7012      0       00000000010 NOR         0& 7

which unfortunately showed up on the listing (page 12) like this:

   0000 0002 0 0000 15 7012      0       00000000010 NOR         0& 7

Alas, that "0&" in columns 66-67 is very significant (meaning "at the current location plus 0"), and since it was suppressed on the listing, was not initially transcribed. That omission changed the intended meaning of the instruction from "Normalize the A register and branch on zero to address ((*+10) mod 20) + 7000" to "Normalize the A register and branch on zero to address ((10) mod 20) + 7000," i.e., to effective address 7010 instead of 7012. Fortunately, this problem was caught due to the difference between the assembled address and the address that was transcribed from the numeric instruction word.

With that rather long introduction to leading-zero suppression for Cardatron output devices and the occasional mischief it causes, this release now supports such suppression. There is an additional button on the control panel for line printers and card punches, labeled SET ZS:

Line Printer window with new SET ZS button

The center of this button will be black if no zero suppression has been configured for the device, and green if zero suppression is currently in effect. Clicking this button opens a sub-window with which you can view, enter, or edit the list of zero-suppression columns for the device:

Zero-suppression panel window

You simply enter a comma-delimited list of numbers that indicate the columns on the output where zero-suppression is to begin. The numbers must be entered in ascending order. Spaces and empty entries (adjacent commas or spaces between two commas) are ignored. To save the list and apply it to the device, click the OK button.

The list of columns is recorded in the system configuration data and preserved across browser sessions. You can view the current list of columns on the System Configuration window, but the list can be changed only through the SET ZS button on the device window.

The recommended list of zero-suppression columns for Knuth's EASY assembler is 13, 14, 15, 20. The Shell Assembler does not require zero-suppression of its output.


Control Panel Display Enhancements


One of the things that has been a lot of fun (and lot of effort) with the retro-205 emulator is the reproduction of the Supervisory Panel and Control Console. Being able to interact with the system in much the same way that programmers and operators did with a real 205 is interesting, and tells us something about the practice of programming, debugging, and operating computers 50-60 years ago.

Both panels operate independently. Every 50 milliseconds or so they interrogate the state of the processor and translate that to on/off settings of the neon lamps on the panel window. That refresh rate (20 times per second) is close to the human ability to perceive visual change (24 frames per second), and about one-third of the refresh rate for most modern computer displays. Even so, the sampling is a binary process -- it results in a lamp being fully on or off for the next 50 milliseconds.

The lamps on a real 205 panel did not behave that way. They were continuously sampling the state of the flip-flops they represented, and since the lamps have a slight persistence, their intensity nicely presented a time-averaged value of the 0/1 state of the flip-flop to which they were connected.

Tom Sawyer saw this early on in the emulator development, and pointed out to me that the Fetch/Execute and Overflow indicators did not behave at all the way he remembered them. I added some code to the Processor to compute an exponential running average of the state of those signals, which yields a value between 0 and 1. I experimented with colors and came up with a set of seven that ranged in apparent brightness from full off (dark gray) to full on (orange, red, white, etc.). It was then easy to convert the fractional average state to one of those colors and display it in the lamp.

That simple mechanism gave a very nice effect, but it only improved the display of those three lamps. I had wanted for some time to extend it to the rest of the lamps on the panels. Last winter, I started experimenting with fractional intensities for the Control Console, and have gradually extended that to the Supervisory Panel as well.

While in principle the idea of computing a running average signal intensity and translating that to lamp color is simple, doing it for the full panels is complicated by two things:
  • There's a lot of lamps, and that requires a lot of calculations. There are 230 flip-flops whose state needs to be time averaged. The Supervisory Panel displays all of those; the Control Console displays 187 of them. The exponential running average calculation is of the form
    v[n+1] = v[n]*a + x*(1-a)
    where:
    • v[n+1] is the updated average for this period
    • v[n] is the average computed for the prior period
    • x is the new value of the state for this period (0 or 1)
    • a (termed alpha) is a decay constant between 0 and 1. Larger values of a diminish the effect of new values of x upon the average; smaller values give new values of x more influence upon the average.
  • Ideally, we would update the average intensities at every clock pulse (7 microseconds), but the emulator does not work at the clock level. It works at the instruction level, attempting to accurately represent the state of the Processor's registers and control flip-flops at the end of each Fetch or Execute cycle. Besides, computing 230 averages 142,857 times per second (corresponding to the rate at which digits cycled through the registers) seems a bit much for a dynamically-typed, interpreted language such as Javascript. The panels don't update that fast, anyway.
Backing off from attempting to model lamp intensities at that low a level, I decided to try doing it during the drum latency that occurs at each memory access. There is drum latency during each Fetch cycle to load the next instruction word into the Processor, and another during those instructions that require an operand from memory. These latencies average 8 milliseconds for accesses to the 4000-word main portion of the memory drum, and 0.8 milliseconds for access to the high-speed loops. That is a more reasonable refresh rate.

It also turns out to give a very nice effect. The partial lamp intensities give a much smoother appearance to the display, and actually look like a computer that's chewing away on some problem. My four-year old 3GHz Pentium i3 desktop does not seem to have any trouble keeping up with the calculations. A bigger limitation appears to be the browser's ability to update the screen fast enough. Not only does the emulator need to perform all those calculations, but the browser needs to translate the style changes that set the lamp colors into the appropriate set of pixels and pump them out to the display. Google Chrome and Safari seem to do this best, with a smoothly updating display that looks very realistic. Firefox does less well, with a chunkier update behavior, suggesting that it doesn't (or can't) update the screen as frequently as Chrome and Safari can.

Although the visual effect was nice, initially there were two problems with this approach. The first was that when the Processor stopped, so did the updating of the average intensities for the lamps. The display would freeze with many lamps showing partial intensities. I solved that by putting in extra code at each place a halt could occur to recompute the averages using an alpha of 0, which sets all the averages to the halted 0/1 state of the flip-flops.

The second problem was that nothing was displaying for the Carry and Adder lamps on the Supervisory Panel. I finally realized that the state of those emulated flip-flops was being set only at the end of a Fetch or Execute cycle, and the Carry and Adder are almost always zero at that point. The Adder works in a digit-sequential fashion, so I solved that problem by introducing code into its operation to update the average intensities for those flip-flops for each digit passing through the Adder instead of once at the end.

While these make the panel displays appear more realistic, it has been tacked onto the emulator as an afterthought, and it's all a bit of a kludge. The flip-flops for the registers on the panels go through many more state changes than we are sampling. It would be nice to do a better job of including those intermediate states in the lamp intensities being displayed, but that would require a different approach to the internal design of the emulator, and probably an implementation for some environment more computationally powerful than Javascript in a web browser.


Other Corrections and Enhancements


This release contains the following additional corrections and enhancements to the emulator.
  • This project has always operated under the open-source MIT License. A copy of that license is now included in the GitHub repository and on the hosting site.
  • A home page with useful links has been added to the project hosting site at http://www.phkimpel.us/ElectroData-205/.
  • A second card punch was added to the standard system configuration as Cardatron output unit #2. This was required for use with Knuth's Algol-58 compiler.
  • Translation for the "lozenge" glyph was corrected for output to the Flexowriter.
  • An annunciator showing the current block number has been added to the magnetic tape panel, just below the spinning reel icon. The tape read, write, and search commands have been modified to update this annunciator as the tape moves. As part of this change tape search was rewritten to allow it to be interrupted more cleanly by a general system clear.
  • The windows for the peripheral devices were being opened twice during emulator initialization. This was initially done to avoid having to manually close a lot of windows when the emulator crashed, but it is reliable enough now that the double-open is no longer needed.
  • The method used to initially position and size the Control Console window was changed to avoid a bug in Google Chrome.
  • When copying lines of output from the "paper" area of a Cardatron line printer device, some browsers (notably Firefox) would insert a blank line between every three lines of text. This was a consequence of using individual HTML <pre> elements to implement the greenbar paper background. The markup has been changed to use <div> elements in a way that avoids the extraneous blank lines.


Future Work


With the implementation of the system configuration capability, there is just one more significant component for the emulator itself that needs to be completed, the Model 560 DataFile storage unit. As mentioned above, this was a type of magnetic tape device, but used multiple strips of tape to achieve semi-random access to data. It interfaced to the Processor through the same Tape Control Unit as the Model 544 DataReader, and was controlled by the same tape instructions.

Much of the mechanics of the DataFile can be taken from existing code for the DataReader, but there are some significant differences. The tape strips were not removable, except for maintenance, so the entire mechanism for loading and unloading tapes goes away. With that, however, comes the need to persistently store the contents of the tape strips across emulator sessions. Read, write, and search work the same as for the DataReader, but lane selection during search also needs to move the tape head to the appropriate strip, a process that could take up to two seconds. Thus, it will be a fair amount of work to clone a DataFile implementation from the existing DataReader one.

After completing the recovery of Knuth's Algol-58 compiler last year, I corresponded briefly with Professor Knuth, who expressed interest in having that program work again, and kindly sent me some additional programs and materials for the 205. Those have been partially recovered and made to run, but more work is needed on a couple of them. These will be described in a future blog post.

Finally, with work on the retro-205 emulator nearing completion, I've started looking for another machine to emulate. The successor to the 205 was the Burroughs 220, a somewhat similar computer design, but with core memory instead of a drum, a much-improved instruction set, and a more sophisticated Algol-58 compiler that was known as BALGOL. A complete listing of that compiler, donated by Professor Knuth, survives on the Computer History Museum web site.

Tom Sawyer has agreed that this blog can be re-purposed as a Burroughs 205 and 220 resource, thus you can expect to see posts on the retro-220 emulator and BALGOL recovery project starting in 2017.

Saturday, June 11, 2016

The Shell Assembler, Part 2

In Part 1 of this post, I introduced the Shell Assembler for the Burroughs 205 [1]. That part discussed the origin of the assembler, an overview of its features, its reconstruction from listings in its Part II manual [2], and how to operate the assembler in the retro-205 emulator.

This is Paul Kimpel, with another post on Tom Sawyer's 205 blog. In this second part, I will discuss programming with the Shell Assembler, along with its relatively advanced features for making the programmer's job easier and more productive. Unlike Knuth's EASY and MEASY assemblers [3], which were built to be lightweight and fast, Shell was built for power and convenience. It became the assembler of choice for shops that had to write and maintain a lot of code.


Operator and Pseudo-Operator Mnemonics


In contrast to EASY and MEASY, which use a set of operator mnemonic codes designed to be compatible between the Burroughs 205 and 220 computer systems, the Shell Assembler uses the standard set of mnemonics initially established by ElectroData and continued by Burroughs in their reference manuals:
     00 PTR     20 CU      40 MTR     60 M       80 FAD
     01 CIRA    21 CUR     41 --      61 DIV     81 FSU
     02 STC     22 DB      42 MTS     62 --      82 FM
     03 PTW     23 RO      43 --      63 EX      83 FDIV
     04 CNZ     24 BF4     44 CDR     64 CAD     84 --
     05 --      25 BF5     45 CDRI    65 CSU     85 --
     06 UA      26 BF6     46 --      66 CADA    86 --
     07 PTWF    27 BF7     47 --      67 CSUA    87 --
     08 STOP    28 CC      48 CDRF    68 --      88 --
     09 --      29 CCR     49 --      69 --      89 --
     10 DAD     30 CUB     50 MTW     70 MRO     90 (FAA)
     11 BA      31 CUBR    51 --      71 EXC     91 (FSA)
     12 ST      32 IB      52 MTRW    72 SB      92 (FMA)
     13 SR      33 CR      53 --      73 OSGD    93 (FDA)
     14 SL      34 BT4     54 CDW     74 AD      94 --
     15 NOR     35 BT5     55 CDWI    75 SU      95 --
     16 ADSC    36 BT6     56 --      76 ADA     96 --
     17 SUSC    37 BT7     57 --      77 SUA     97 --
     18 --      38 CCB     58 CDWF    78 --      98 --
     19 --      39 CCBR    59 --      79 --      99 --

It also implements a number of synonyms for some of the instructions, e.g., A for AD (Add) and S for SU (subtract). For some reason, the version of the assembler we have does not implement mnemonics for the floating-absolute instructions (90=FAA, 91=FSA, 92=FMA, 93=FDA). It would not be difficult, however, to add these to the assembler's symbol table.

In addition to supporting the mnemonic operator codes for the 205 machine instructions, the Shell Assembler offers these pseudo operators:
  • REM: a remark or comment. The remaining contents the card are ignored by the assembler, except that they are printed on the listing.
  • EQU: assign a value to a symbol. This is typically used to assign a symbol to an absolute address, or to assign the value of one symbol to another.
  • ORG: specify the next assembly address. Normally, the assembler increments its address counter by one after assembling an instruction. This pseudo operator is typically used to establish a starting address for a program or to position a section of a program at a particular address.
  • REG: reserve a block of memory locations. This simply adds the value of the operand field to the address counter. Any label for the line is assigned the value of the starting address of the block
  • MOD: advances the address counter forward as necessary so that it is a modulo 20 address. This is particularly useful for positioning the address of magnetic tape records in memory. Data transfers to and from tape can begin at any main memory address, but because magnetic tape I/Os are buffered through the 4000 and 5000 high-speed loops on the memory drum, and because of the way that blocking transfers between main memory and the high-speed loops work, the word at the first mod-20 congruent address is the first one read from or written to the tape. Thus a program must read data from a tape using the same mod-20 congruent address as was used to write data to the tape in order for the words end up in the same sequence in memory. Starting the memory buffers on mod-20 addresses helps eliminate any confusion as to the word ordering between tape and memory.
  • NUM: define a one-word numeric literal value. The value must be right-justified in the 10-character operand field, with the sign coded as either 0/1 or +/- in the tag field. Spaces in the operand and tag fields are interpreted as zeroes. See the "Input Card Layout" section below for the field locations.
  • ALF: define a one word (five character) alphanumeric literal encoded using Cardatron card codes.
  • ALFS: define an alphanumeric literal up to six words (30 characters) in length, encoded using Cardatron codes. The text of the literal is written in the remarks field of the card. The length of the literal in words (1-6) must be written right-justified in the address portion of the operand field. The resulting words are stored in reverse order in memory to support both the way the Cardatron accessed memory and the decrement-and-branch indexing native to the 205's B register.
  • FLX and FLXS: similar to ALF and ALFS, but generate words using the older Flexowriter code used on the Datatron 204 and earlier system models before the Cardatron was introduced.
  • TAG: specify the high-speed loop to which addresses in assembled instructions should be assigned. Efficient programming for the 205 requires that instructions be transferred to one of the high-speed loops (typically the 7000 loop) and executed from there. The TAG operator specifies the digit (4-7) that the assembler will automatically set in the high-order digit of the operand address for instructions that reference memory, saving the programmer the trouble of continually having to override that digit on individual instructions. This behavior can be overridden on individual instructions as described for the Loop Tag field below, or disabled altogether by coding a TAG operator with an "X" as the tag digit.
  • MACRO and NEW: define assembler macro instructions. The difference between them is that MACRO is used to define a temporary macro that will exist only for the current assembly run, while NEW defines a macro that can be used in the current assembly run, but will also be inserted into the macro library on magnetic tape. See the "Macro Definition and Usage" section below for more information on how these two pseudo-operators are used.
  • SUBR: causes a subroutine to be copied from the macro library and appended to the end of the program being assembled. No call linkage to the subroutine is provided. A given subroutine will be included only once, no matter how many references are made to it using SUBR. Thus a body of code can be written as a closed subroutine, and a macro written to provide parameters and linkage to the subroutine, using SUBR within the macro to call out the closed body of code from the library. Each macro invocation will generate unique linkage code at that point in memory, but the body of the closed subroutine will be included only once. Subroutines can be added to the library as if they are a parameter-less macro, using NEW.
  • RFB, WFB, and PFB: generate Cardatron format bands. RFB generates a format band for reading cards; WFB generates a format band for printing to a line printer; PFB generates a format band for punching cards. These operators use a simple notation to define the card or print line format, dramatically reducing the effort and potential for error compared to coding a band manually. See the "Cardatron Format Band Assembly" section below for more information.
  • END: signals the end of the main program or a macro. The card deck must always end with this pseudo-instruction.
As mentioned in Part 1 of this post, the Shell Assembler does rather extensive checking of the input text and assembled code, generating error messages in English and identifying the line on which they were detected. It also does "invalid pair" checking -- flagging instances of one instruction following another that are unreasonable or which could lead to incorrect results.


Programming with the Shell Assembler


Source program input to the Shell Assembler was punched on cards. This section describes the layout of the cards, how the fixed columns of that layout were used, and some special features of the assembler.

As described in an earlier post [4], the Cardatron input unit requires a punch in a specified column to select a format band from its small drum memory. The codes in this format band describe how the Cardatron will reformat and translate columns of data on the IBM card machine to the 205 memory. The Shell Assembler normally takes its band selection from column 1 and uses two format bands:
  1. Format for instruction cards.
  2. Format for a header card.

The Header Card


The first card in the deck must have a "2" in column 1, indicating it is the header card. The contents of this card are read using the format band defined in FMB7 of Movement 1 and output to the line printer using the band defined in FMB6. The contents of this card are not otherwise used by the assembler.
  • Columns 1-3 are read as a number.
  • Columns 4-53 are read alphanumerically (these are probably intended to be a title).
  • Columns 54-55 are also read alphanumerically.
  • Columns 56-61 are read numerically (these appear to be intended as a date in MMDDYY format).
  • The remaining 19 columns on the card are ignored.
 

Input Card Format


The remaining cards of the source program must have a "1" in column 1. These cards carry the instructions that comprise the program.

Cards for format band 1 have the following layout:

     1       10        20        30        40        50        60
     .........|.........|.........|.........|.........|.........|.....
     1        LLLLLSSSSSCCCCCTAAAAAIIIIIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR

Columns 2-9 and 66-80 are ignored by the assembler, and can be used for sequencing or identification of the deck.

Column 1: Cardatron format band code. The character in this column is used by the Cardatron, but otherwise ignored by the assembler.

Columns 10-14: Symbolic location label: the traditional label field for an assembly line. The symbol in this field is assigned the current value of the address counter. Labels are limited to five characters and must be unique within the program. They must be left-justified and start with an alphabetic character, but any characters, including punctuation, can be used in the other four positions.

Alternatively, this field could have a "program-point" label, written left-justified in the field as a period followed by a letter, e.g., ".A". Program points are temporary labels with a relative, local scope. They can be defined multiple times within a program. The program can refer to an address at the immediately-prior or immediately-next program point. Once a program-point label is redefined, its prior location falls out of scope and can no longer be referred to by subsequent instructions. See columns 26-30, below, for the way that these labels were referenced.

Program points allow a programmer to refer to locally-relevant locations, e.g. for short branches and local constants, without cluttering up the symbol table with labels that may be used only a few times within a small region of the program.

As discussed in a footnote on page 2 of the Part I manual for this assembler, the idea of program points apparently originated with Melvin Conway of the Case Institute of Technology (now Case Western Reserve University) and were first incorporated into the SOAP III assembler for the IBM 650 by Donald Knuth. Knuth later used program points in his EASY and MEASY assemblers. They also found their way into a number of later Burroughs assemblers. Program points were not available in STAR 0, the official Burroughs assembler for the 205, however.

Columns 15-19: "Spares": the sign and first four numeric digits of an instruction word. The sign must be coded in column 15, and can be a numeric digit, a space (which was translated to 0) or "-" (which was translated to 1). Odd-valued sign digits in instruction words cause the B-register to be added to the operand address to form the effective address used when the instruction executes. The other four digits are used for the breakpoint control digit (in column 19) and for control digits in I/O instructions. Programmers sometimes used this field for data. If all five columns are left blank, the sign and control digits are assembled as zeroes. If any of the four control digits are non-zero, all four must be coded.

Columns 20-24: Operation code: the symbolic operation code for the instruction or pseudo-instruction. This must be left-justified in the field. Alternatively, a numeric operation code can be entered in the last two positions of this field. The Shell Assembler uses the standard ElectroData/Burroughs mnemonics as discussed in the prior section.

Column 25: Loop tag: for instructions that reference memory addresses, a value of 4-7 in this column will cause the high-order digit of the assembled address to be set to this value, making it an address for one of the high-speed loops on the drum. If this field is left blank and a loop digit has been specified previously by the TAG pseudo-instruction, that loop digit will be placed in the high-order digit of the assembled instruction. If column 25 contains an "X", any effect of TAG is suppressed in the assembled instruction, and the high-order digit of the address is assembled normally.

Use of the TAG pseudo-instruction considerably eases the task of writing code to run in the 205's high-speed loops. Instructions must be assembled into main memory locations and then blocked to one of the loops. Instructions referencing locations within the loop, e.g., for short branches or local constants, must use loop addresses instead of the ones that would normally be assigned for their originally-assembled location, so the use of TAG allows this address modification to be applied automatically. Most of the code in the Shell Assembler itself is written under the influence of a TAG 7 directive, as most of the code is blocked into the 7000 loop by CUB instructions for execution.

Columns 26-30: Operand address: the coding in this field specifies the address field to be assembled into the instruction (possibly modified by the increment field below). This field can hold one of the following:
  • Spaces: this causes the current value of the location counter to be assembled into the instruction. This is equivalent to the symbol "*" used by many other assemblers to designate the current value of the location counter.
  • Absolute address: a literal, decimal address, right-justified in the five columns of the field. Leading zeroes could be replaced by spaces. This was normally used only with instructions that require a numeric operand, such as shifts and console I/O.
  • Symbolic address: one of the alphanumeric symbols defined in columns 10-14 on some other line of the program.
  • Program-point address: a reference to a program-point label. This is coded in columns 26-27 as either "+L" or "-L", where "L" is a letter. "+L" refers to next line in the source labeled ".L". "-L" refers to the prior line in the source with that label. Only the immediately-next or immediately-prior lines with that label can be referenced with this notation. Note that, depending on the type of print wheels installed in the 407, "+" (a 12-row punch on a card) may print as "&".
Columns 31-35: Operand address increment: If this field is blank, the operand address specified in columns 26-30 is assembled as is. Otherwise, the value of this field modifies that operand address. Column 26 must be coded as a "+" ("&") or "-". The remaining four columns specify the increment, right-justified in the field. Leading zeroes may be replaced by spaces. This increment is then algebraically added to the operand address, modulo 10,000. Negative results are represented in 10s-complement form, e.g., 0000 - 0001 is 9999.

Columns 36-65: Remarks: These 30 columns can be used for comments, and are reproduced as-is on the output listing. This field is also used as the operand for the ALFS, FLXS, RFB, PFB, and WFB pseudo-instructions. Format-band coding for the three format-band instructions can overflow on up to eight continuation cards by leaving the operation code field on the continuation cards blank.


Cardatron Format Band Assembly


One of the most useful features of the Shell Assembler is its ability to construct Cardatron format bands from a fairly simple and intuitive notation.

Designing and coding format bands for the Cardatron is extremely tedious. As described in [4], a 205 format band consists of 315 digits, which occupy 29 words in memory before being loaded to the Cardatron drum. Bands are coded using values 0-3 in each digit position of a memory word, including the sign digits.

The Cardatron works by splitting each column on a card or line of print into two decimal digits, one for the numeric portion (the bottom nine rows on a card) and one for the zone portion (the top three rows on a card). The format band digits operate on the stream of numeric and zone digits coming from or going to the IBM device, by copying digits, deleting digits, or inserting zero digits into the stream. The result is a mapping between the 80 or 120 columns on the IBM device and up to 28 words in 205 memory. The format band digits can drop columns and insert sign, leading, and scaling digits on input. They can also insert blanks on output, control whether digits from memory words are output numerically or alphanumerically, reduce 10-digit memory word values to fewer columns on a card or print line, and position signs as either separate columns or overpunches.

This mapping is complicated by the order in which the Cardatron processes data. It starts with the last column on a card or print line and works towards the front, with the first word in memory mapped to the last columns on the card or print line. Thus the low-order digits in the first word of a format band in memory references the end of a card or print line. The band designer must code digits to account for exactly 80 columns on a card or 120 columns on a print line, and provide "3" (delete digit) codes in all remaining positions of the format band. On input, the designer must code a single additional "0" (insert zero digit) code at the end of the format band to push the last word being assembled out of the D register and into memory.

Electrodata developed a tabloid-size (11x17-inch) form to aid in designing and coding format bands. See page 6-11 in [5] for an example of the form used with the 220 Cardatron, which is almost identical to that for the 205. This form made it easier for the designer to keep track of columns on the IBM device, the mapping between those columns and words in 205 memory, and any padding required to fill the band data out to 29 memory words. Even with this form, though, the process of designing and coding the band digits was tedious and error-prone.

The format band pseudo-operators in the Shell Assembler dramatically simplify the task of designing and coding format bands. They use a free-field notation with letter codes and repeat factors, not unlike a FORTRAN FORMAT statement or a COBOL PICTURE clause. The band definition consists of phrases, one per 205 memory word, that define the mapping between digits in the memory word and columns on the IBM device. The word-phrases are delimited by "+" (or "&") characters. For example, consider the definition of format band 1 described in the prior section that is used to read the instruction card for the Shell Assembler:

FMB1     RFB            9B & 7¤P10Z¤ & 3¤P5A¤ & P8ZA
                           & 8¤P5A¤ & P10Z & 15B
The codes used in format band phrases have the following meanings:
  • A - translate a column alphanumerically. On input, two digits from the card (usually the numeric and zone digits from the same column) are translated to the Cardatron code and stored in the next two digits in memory, proceeding from right to left in the memory word. On output, two digits in Cardatron code are translated from the memory word to numeric and zone codes for the next column (again, proceeding from right to left) on the card or print line.
  • B - ignore a column. On input, the next two digits from the card are dropped and not stored in memory. On output, two zero digits are generated, producing a blank column on the IBM device.
  • N - translate a column numerically. On input this copies the numeric digit from a card column to memory and discards the zone digit. On output, it copies a digit from memory as the numeric digit for a column and supplies a zero for the zone digit in that column. As a special case on output, if the digit in memory is a zero, a zone digit is output that causes a zero to be printed or punched in the column. This was necessary because the IBM devices consider  a zero punch (third row on a card) to be a zone, not a numeric punch. Without this special case, zeroes in memory would be output as blank columns instead of zeroes.
  • P - ignore the sign digit in memory. This code is identical to the Z code below, except that the assembler will allow a P only for the leftmost digit in a word. The combination of a special code for the sign digit and delimiters between word-phases allows the assembler to verify that exactly 11 digits have been generated or consumed by the band for each memory word.
  • X - transfer zone digit. This is normally used to place the sign in a column. The combination XN will treat the sign as an over-punch on the numeric digit formatted by N. The combination XB will treat the sign as a separate column.
  • Z - ignore digit position in memory. On input, this will not consume a digit coming from the card and will store a zero digit in memory. On output, it will skip the next digit in memory and output nothing to the IBM device.
  • ¤ - the lozenge (which prints as a right-parenthesis on some tabulators) serves as a bracket character to group one or more phrases for repetition.
Any of these codes can be prefixed by an integer to designate that it will be repeated that number of times. A repeat count in front of a ¤-group indicates the entire group is to be repeated that number of times.

For the format band example above, the word-phrases can be understood to have the following meaning:
  • 9B - skip the first 9 positions on the card.
  • & 7¤P10Z¤ - store seven words of zeroes into memory. The P stores a zero in the sign-digit position and 10Z stores the other ten digits of the word.
  • & 3¤P5A¤ - transfer the next 15 columns on the card alphanumerically to the next three words in memory. This corresponds to the Symbolic Location, Spares, and Operation Code fields in columns 10-24 on the card. The P stores a zero in the sign digit of each five-character word.
  • & P8ZA - Store the alphanumeric code for next column (TAG in column 25) in the low-order two digits of the next word in memory. P8Z stores zeroes in the sign and high-order eight digits of that word.
  • & 8¤P5A¤ - Transfer the next 40 columns (26-65) from the card alphanumerically into the next eight words in memory. This comprises the Operand Address, Address Increment, and Remarks fields on the card.
  • & P10Z - Store another word of zeroes into memory.
  • & 15B - Skip the remaining 15 columns (66-80) on the card.
Note that the additional zero digit to push the last input word out of the D register is supplied automatically by the assembler when translating a RFB defnition.

The way that format bands are coded internally for input and output is slightly different. One very nice thing about the format-band phrase notation is that it is symmetric between input and output. Reading a card from an input device with a set of format phrases in an RFB specification and then writing that data using the same set of phrases in a WFB or PFB specification produces an identical set of columns on the output device. The digits in the format band will be somewhat different, but the assembler knows how to code the band differently based on the pseudo-operation code used.

The only difference between WFB and PFB is that bands for punching cards must format 80 columns, while bands for printing must format 120 columns. The band designer must still account for the appropriate number of columns on the IBM device, and the assembler will verify this, issuing an error message if the number of columns is incorrect. The designer does not need to worry about padding the band out to 315 digits, however -- the assembler does that automatically.


Macro Definition and Usage


Our word macro comes from the Greek word for long. In the context of computers and software, macro is typically used as an abbreviation for macroinstruction. There are many forms of macroinstructions, ranging from simple text replacement (e.g., named constants) to parameterized replacement of text (e.g., the C #define pre-processor) to the conditional and iterative generation of highly customized text.

Macro assemblers are assembler programs that implement the ability to create and invoke macroinstructions. The first assembler to employ macros appears to be Autocoder for the IBM 702 and 705 in 1956. The presence of macros in the Shell Assembler of 1958 is therefore a fairly early application of this concept. There were people at Shell at the time who had come from IBM, including Bob Barton, so it's possible that the idea of including macros in the Shell Assembler came from those former IBMers.

The main idea of macros in assembly languages is to take commonly-used sequences of instructions and encapsulate them, give that sequence a name, and call forth the sequence later, possibly specifying parameters that will customize the sequence at that point in the program. The invocation of a macro often looks like the coding of an instruction, but with the operation code naming the macroinstruction instead of the mnemonic for a machine instruction.

Macros and subroutines are related, in that both can be used to define a sequence of code once and then activate that sequence from many places later. Subroutines are generally stored in memory only once, the program transfers control to the subroutine when it is invoked, and the subroutine passes control back to the program when it is finished. Macros, on the other hand, generate code in the program at the point they are invoked. Each time the macro is invoked, additional code is generated at that point in the program, and the code may be customized based on the parameters passed as part of the macro invocation.

Another important difference is that while subroutines are executed at run time, macros are executed at assembly time -- invocation of a macro generates additional text to be assembled. The instructions resulting from assembling that additional text will be executed at run time, but not the macro itself.

Invoking Macros in the Shell Assembler


A macro in invoked by writing its name in the operation code field (columns 20-24) on a card. A label, if desired, can be written in the label field. Actual parameters to the macro, if any, are written in the operand address and increment fields (columns 26-35) just as they are for a normal instruction. The first parameter is written in the operand field to the right of the macro name. Any additional parameters are written in the operand fields of subsequent cards, one per card. The operation code of these continuation cards must be blank. There is no limit to the number of parameters a macro may have.

All forms of operands may be passed as macro parameters. The only exception to operand coding for macros as compared to normal instructions is that the loop tag is never applied to the parameter operand as part of the invocation -- a loop tag may be applied to the parameter when it is used inside the macro, of course, but operand parameters are passed to the macro as is, without loop address modification.

Thus, a macro instruction with three parameters might be coded as follows:
.G    CLEAR         0020
              TBL1 +0012

                   +0001
As discussed above, invocation of a macro generates code in the program at the point of invocation. The Shell Assembler does this a little differently than most macro assemblers, however. At the point of invocation, the assembler emits a CUB (30, Change Unconditionally and Block) instruction. The code generated by the macro is placed at the effective address of that instruction, which will be after the end of the non-macro instructions in the program.

Any return to the point of invocation is the responsibility of the macro, so it was typical to pass a label or relative address as one of the parameters to the macro. An example of this is shown in the third parameter above -- the +0001 is relative to the CUB instruction emitted by the assembler at the point the macro is invoked. Thus the return would be to the instruction immediately following the invocation.

The reason for using a CUB at the point of macro invocation is that, once again, efficient programming for the 205 requires that code be executed out of one of the high-speed memory loops as much as possible. Well-written 205 programs were coded mostly as short sequences of instructions linked by CUBs to load and execute the sequences. Thus, macro invocation automatically puts the generated code in the 7000 loop. It is the macro coder's responsibility to deal with macro expansion that exceeds the 20-word capacity of the 7000 loop.

Defining Macros in the Shell Assembler


Macros are defined using the pseudo-operators MACRO and NEW. MACRO defines a temporary macro that will available only during the current assembly run. NEW defines a macro that will be permanently cataloged on the library tape. Other than that distinction, the two pseudo-operators are identical and function the same. MACRO and NEW can also be used to define bodies of code to be called out using the SUBR pseudo-operator, although such definitions should not use parameters.

Temporary macros are stored on the library tape along with permanent macros; they are simply not recorded in the tape's catalog. For that reason, Section VII of the Part I manual recommends that MACRO definitions follow any NEW definitions to avoid leaving unusable gaps on the library tape. It also recommends that permanent updates to the library be done in a separate assembly run dedicated to that purpose.

To define a macro, code MACRO or NEW in the operation-code field and the name of the macro (up to five characters) in the operand address field of a card. In the middle two digits of the "spares" field (columns 17-18), code the number of cards that make up the macro definition, not including the MACRO/NEW card, but including the required END card that terminates the macro. The names of newly-defined macros must not match the name of any existing macro -- attempting to do so results in an error.

Within the body of the macro, instructions are written the same way they are in open code. Macros may invoke other macros. Parameters are referred to by their ordinal position in the invocation -- #0001, #0002, #0003, etc., and may be referenced as the operand address in instructions and other macro invocations. The effective address of these parameters may be modified by the operand address increment field and by means of loop tags.

Here is one possible implementation for the CLEAR macro invoked above. Note the count of cards in the first line of the macro definition (0080) and the negative sign on the second STC instruction, causing it to apply B-register modification to the operand address.
      0080 MACRO  CLEAR
           TAG  7
           SB     +C
           STC    +C
     -     STC  X #0002
           DB          -0001
           CUB  X #0003
.C            00X #0001-0001
           END
When a macro invocation is encountered during Movement 1, the assembler emits a CUB with an unresolved forward address at that point in the program, then searches the macro library for the macro, and when found, makes an entry in a memory table for the macro and its parameters. After the END card is sensed in Movement 1, the assembler steps through this table, and for each macro invocation, re-positions the library tape to the macro in the library and appends its body on tape unit 1 after the records that were written there for the main portion of the program, partially assembling the instructions in the body in the same way that instructions in the main portion are during that first movement. In Movement 2, assembly of the instructions from the macro bodies is completed as for other instructions, and the addresses in the CUB instructions at the point of macro invocation are resolved to the location where the macro bodies are assembled.

The project repository has a folder [6] with small sample card decks and assembly listings, showing both definition and invocation of macros. The listings show how the CUB instructions emitted at the point of invocation branch to the code appended at the end of the program for each invocation.

Recovery of the Shell Assembler has been a very interesting part of this project. I wish we had more examples of its use in significant applications, especially ones demonstrating the macro facility, and perhaps some will surface in due time. In any case, it's an excellent example of a sophisticated piece of programming from the mid-late 1950s.


References

[1] Shell Symbolic Assembly Program for the Burroughs 205, Part I, Burroughs Corporation, Bulletin 3038, March 1960:
https://www.dropbox.com/s/gnsdrwors4j3mnf/Shell-Symbolic-Assembly-Program-for-the-Burroughs-205-Part-I.pdf?dl=0

[2] Shell Symbolic Assembly Program for the Burroughs 205, Part II, Burroughs Corporation, Bulletin 3038, March 1960:
https://www.dropbox.com/s/lc3317ptvuec199/Shell-Symbolic-Assembly-Program-for-the-Burroughs-205-Part-II.pdf?dl=0

[3] "Knuth's EASY Assembler":
http://datatron.blogspot.com/2015/08/knuths-easy-assembler.html

[4] "The Mighty CARDATRON":
http://datatron.blogspot.com/2015/03/the-mighty-cardatron.html

[5] Burroughs 220 Operational Characteristics, Bulletin 5020A, Burroughs Corporation, August 1960:
http://bitsavers.org/pdf/burroughs/electrodata/220/5020A_B220_OperCharac_Aug60.pdf

[6] Folder of sample macro definitions and invocations:
https://github.com/pkimpel/retro-205/tree/master/software/Shell-Assembler/Shell-Macro-Tests