Building Quick Disk Drive, work on line printer and card reader, week ending March 23, 2014


I wired up the hopper empty switch as a direct signal, using this instead of the hock (hopper check) error that the Documation reader raises, because the hock alert comes too late in the card cycle. The hopper empty switch is flagged within a millisecond of the last card clearing the switch, while it is still only halfway out of the hopper and its front edge is not even at the photocell station yet.

I continued the debugging and testing, finding that the last card status works properly - if the hopper is emptied, the reader resets the ready lamp. If the start button is pushed without adding cards to the hopper, it switches on ready and marks the next 'read' from the processor with the last card status bit.

It is not properly handling the restart from an empty hopper, if additional cards are put in the hopper and start subsequently pushed. This should allow reading to continue, but the adapter has not cleared the 'busy' and 'not ready' bits in the DSW. Once I clear up this bug, all the normal mode behavior of the reader will be finalized, working exactly as a real 2501 would behave on a real 1130.

The remaining testing is for error conditions - forcing various feed checks and read checks - making sure that the virtual 2501 reader does the appropriate thing to reflect the error condition in the way a real 2501 would present it, and that the operator recovery procedures used on a physical 1130 system will work properly when used on my reader.

Testing with the last card sequence showed that the adapter does switch into the last card state, illuminating the ready lamp and setting the DSW bit for last card, but the next XIO Start Read is not accepted. The DSW reports last card plus busy, rather than doing a read then giving an end operation + last card. I configured the signals to trace with the logic analyzer, to narrow down the areas of investigation until I could locate the source of the problem.

I also tested up to the point of setting last card, but instead adding more cards and pushing the start button. This should ready the reader, turn off busy, and cause the reader to continue reading under program control until it next runs out of cards.

What I discovered is some pathological state my adapter gets into, probably a mixture of the IBM adapter logic and mine due to imperfect modeling of a 2501 reader. The result is that the adapter is continually emitting the pick signal, which drives the Documation to run through all its cards. This occurs even with the processor in single instruction mode and without it issuing additional XIOs.

The timing of the hopper empty signal that causes the IBM 2501 adapter logic to shut off ready is not the same as with a real 2501. Either I figure out how to control the timing when it is presented to the adapter or I need some other means of restoring a sane state. Job one is to dig into this until I know exactly what states are wrong or what actions are occurring or not occurring.

It appears that there is a flaw in my synchronization logic between the virtual 2501 that is driven by the 1130 adapter logic and the real Documation that is driven to fill the FIFO that links virtual to real. I need to look at this when I am fresh, plotting out all the cases that can exist to see what changes will tighten this up.


The APL typeball, a type 988 that works with the 1130, arrived from Rolf Levenbach today. It was securely shipped, arrived safe and sound, and is a pleasure to behold. I can't wait to run APL/1130 to see it generating the console typewriter output a user would see on the IBM built 1130. It only places alphabetic characters on one side of the ball, to free up more of the precious 88 character positions on a Selectric type element that can be used for APL characters. Even so, the language has more characters than will fit, but the others are formed by overstriking the same spot with a combination of characters from the ball.

APL typeball (988) for use on IBM 1130

For instance, comments are delineated by the 'quote quad' character  seen below, is formed on the Selectric mechanism by typing with two characters, placing an apostrophe ' over a box character . The typeball strikes one of the characters, backspaces, then strikes the other, leaving the combined character on the screen. Most modern browsers will display those characters above, but if yours doesn't, you will need to imagine the effect.

Quad (box) visible at lower right,  combines with apostrophe to make Quote Quad character


I soldered a second voltage shifter which will take the second serial port of the Arduino Mega 2560 that is my line printer adapter, and convert its TTL voltage levels to the higher RS232 levels. This allows me to hook that serial port to the serial port on the fpga board. The 1403 printer adapter logic on the fpga will communicate to the line printer though the Arduino.

I also produced a cable, but have to finish the fpga side logic before I can hook it up to the board and begin communicating over the link. I did some work but with coding underway on both disk and line printer, coding on the Arduino for the disk, and debugging of the card reader, all these tasks move forward in spurts as I switch from job to job.


I have coded an Arduino Mega 1260 with a modified SD card shield to act as the disk drive for my system. The code allows me to format up to 100 virtual disk packs, load contents into the pack, display records from a pack and mount a pack so that it requests from the 1130 will read and write from that virtual pack.

Arduino Mega 1280 with SD card shield, emulating 2310 Disk Drive and 100 disk cartridges
To copy over the disk pack contents for various 1130 system software, I will make use of a utility program provided by Brian Knittel as part of his 1130 simulator software package. The checkdisk.exe program runs on a PC and among its features is a command to print some or all sectors of the disk file. His simulator has the DMS/2 operating system as a file (dms.dsk), as well as APL/1130 and other software, plus the tools to build my own packs virtually on the simulator. Each of the xxx.dsk files can be printed, listing the hex contents of each sector in a structured format.

My code to load the packs is parsing the DOS file produced by the checkdisk.exe program, picking off the sector number from the header on each sector and then extracting the 320 words listed in hex. The program then writes the sector to the selected virtual disk pack. Once completed, the SD card on my Arduino has an exact copy of the disk contents that were listed. More exactly, I capture the output of the checkdisk.exe program to a DOS file, then play that through a serial port terminal program over the USB link to the Arduino where my code processes the incoming data.

Example of output from Brian Knittel's checkdisk.exe program on DMS2 pack
I have this working well with the serial monitor of the Arduino IDE, but now want to switch to something automated to send one line of text at a time from a file on the PC each time a > prompt is received. Lots of software used to be available to do this, e.g. Kermit or Hyperterminal, but those have gone away or are now obnoxiously priced products. I didn't want to have to write something in Python or C or Java just to do this simply utility work, but am having trouble finding anything ready made and reasonable.

I have not completed the code to communicate with the 1130 fpga, nor the logic in the fpga to capture the XIO requests from programs and move data between 1130 and the disk. I have been carefully testing the Arduino program as I complete each functional section, so that it has good error checking and clean behavior.

Tera Term is the program I chose to use to upload the disk images. I began with the DMS 2 system that is the normal workhorse, where one typically runs Fortran and Assembler jobs and most prebuilt software. After that is loaded I will make versions of the APL/1130 system disk and then of one featuring RPG. I began loading by using some delays configured to add 20 ms to each character and 1200 ms to each end of line, although the pace at which it loads the sectors is pretty slow. I ran through 200 sectors (out of more than 1600 on a pack) but that required almost three hours to complete.

The best solution is to learn the scripting language that comes with this package and set it up to wait for my > prompts before sending each line. In the interim, I experimented with reducing the delays, as long as the operation remains reliable. I couldn't get below 1.2s delay per line without it missing characters, but I was able to cut the per character delay to zero. At this rate, it does just over two sectors per minute, thus would require about 12 hours to load an entire disk pack. I am running this in sections, an hour or two at a time.

I wasn't thinking clearly or I would have realized that these packs wouldn't require the full 12 hours to load, since they are not full! The current process with the line delay as a safety buffer will load a typical disk cartridge in about 6.5 hours, running in the background on a laptop, which is tolerable since loading from PC files to cartridges should be a rare situation. Updates can be done on specific ranges of sectors, further reducing the load time if a modified pack image has to be reloaded.

The formatting process I use sets up all the sectors of the virtual pack with the sector address in word 1 and zeroes for the remaining 320 words. That matches the unused portions of the disk. My DMS2 pack ended well less than halfway through, the remainder of the pack to the end consisting of zeroed sectors. I simply had to stop the load process when I finished the last non-zero sector.

2315 Disk Cartridge used with IBM 1130 - 100 of these contained in the SD card
DMS puts temporary files immediately after the end of the fixed content, which is kept packed down towards cylinder zero. Those 'working storage' files are either deleted or the fixed area is moved upwards to add that file into the section with the other permanent ones. The process of creating the DMS2 pack built a system that had not yet run much work using that pack, thus having almost nothing in the working storage area.

If it had been used for a long time and had jobs with very large or numerous temporary files, I could have had non-zero sectors that I would be copying even though they are not ever going to be read again. A possible solution is to run the DUP program against the disk and identify the start of WS from the listing. That would be the point from which we no longer loaded sectors, leaving them zeroed from the formatting.

Next up was the APL/1130 disk image. This ended at sector 719 out of 1624, similar to the DMS2 disk, and could be loaded in a similar amount of time. In the midst of running it, I discovered that it was pretty sparse in the middle, with long stretches of all zero sectors. Some manual editing of the loader file first let me slash this load work to about three hours.

RPG is a more traditional DMS 2 based system, therefore it packs contents down on lower address on the disk and should be zero mainly above the WS line. I expected it to require a more normal

I selected virtual pack 2 for DMS 2, virtual pack 10 for APL, and virtual pack 30 for the RPG system, choosing the numbers in part so that I was unlikely to accidentally enter the wrong number as a typo or have a partial entry get picked up and used. If I had used pack 20, but paused too long after pressing the 2 key, the Arduino might see a 2 as the selected pack number. That could be bad, wiping out my main DMS 2 cartridge.

I am about a third of the way done coding the two ends of the SPI link between the 1130 fpga and the Arduino, having defined a protocol and started instantiating the various state machines and mechanisms. I chose to have the Arduino preread and buffer eight sectors whenever the drive moves to a new cylinder, then ship them to the fpga which in turn will store them in anticipation of an XIO read from the 1130.

The memory is tight on an Arduino, particularly to maintain almost 5K of sector data. I am coding the machinery to buffer all eight in the Arduino, because it can be fetching these from the time a seek is issued. The Arduino takes about a third of a second to fetch all eight sectors into RAM, which does not include the time it will take to transfer each sector over the SPI link. I believe that process would add 5 ms per sector. That corresponds to about 1/8 of a rotation of a 2315 disk platter or half a sector of area.

However, the 333 ms delay in reading the sectors from SD card prior to any transmission equates to a seek of about 40 cylinders, 1/5 of the pack. Any seek shorter than 40 cylinders would have allowed reading on a real 2310 but we will be delaying the seek completion interrupt. Sequentially reading sectors will appear to occur at real 2310 speeds if I have the sectors held in RAM on the fpga.

The key to improving performance fidelity would be to fetch and transmit the sector that will be requested first by the 1130, then prefetch subsequent ones in sequential rotation and head switching order. We would have the first sector down into the fpga in about 40ms if we knew which was coming in the XIO Start Read. A seek of 3 cylinders or more would totally hide this delay. The problem is knowing the right sector to handle first.

The seek command only indicates the cylinder to which we 'move the disk arm', it does not tell you which of the two heads (sides of the platter) or which of the four sectors rotationally around the disk will be requested next. I can gamble and fetch the sectors for the same head that we are currently using,.On any seek to a new cylinder, other than a sequential read going from last sector of track x to the first sector of track x+1, I think the distribution of head selection is almost random.

The fpga is simulating the rotation and knows what sectors will arrive at the 'arm' at various points in the future. Thus, I could calculate the time when the seek completion interrupt would be triggered, then request the sector which will arrive at the head 40ms from that point as the first sector to read, with the following sectors being fetched later. What I could get wrong is the head to be used. I intend to alternate heads as I fetch sectors after beginning with the selected initial sector.

A longer seek will ensure that I have already transferred the sectors into the fpga. Basically each 2.5 cylinders of seek gives us another sector fetched, where the probability of missing the timing of a real 2310 runs from almost certain at 1 cylinder down to very low at 30+ cylinders and no chance at all by 40 cylinders distance. On average, the additional delay won't be much, because the 1130 system spends a lot of time seeking between system file locations and the WS area, plus some time sequentially reading/writing.

With a typical disk cartridge putting WS at cylinder 80, plenty of opportunities for 30+ cylinder moves. We fall behind on sequential read/write, but only on a fraction of IO and increasing the completion time of those delayed IOs only fractionally.

I am going to think carefully about the various schemes for reading the sectors and serving them to the 1130 - whether to read all 8 at once, what order to read after a seek, whether to copy all eight to fpga unilaterally or whether to push them on request, and even whether to keep only one buffer in the Arduino and sequentially read then push each sector rather than read eight, push eight.

2310 Disk Drive inside 1130 plus additional in external case

No comments:

Post a Comment