Almost no time on Wednesday but got to do a bit more on Thursday. Fortunately I am retiring so I won't have the day job to get in the way. The only short term distraction is the need to take care of a million details, but I should be back testing by Monday.
1442 CARD READER/PUNCH RESTORATION
My friend Marc is offering to mill replacement wheels for the 1442. I had cracked the ceramic rim of one wheel in the punch unit, which rendered the punch inoperative and probably the entire 1442 unusable. I am working on a repair using UV hardening material, but if that doesn't work, having a fallback is good.
SAC INTERFACE FOR ADDING PERIPHERALS TO THE 1130
Resilient High Speed Link between master and slave FPGA boards
Testing of the high speed link can be done with only the SAC box and slave FPGA active. I don't need to power up the 1130 at all. Once I get this link working to my satisfaction, I can move on to the devices connected via the first slave fpga board. I would add one or two additional slave boards when I have devices to control that each have many signals to connect.
I tested at lunchtime Thursday and see that my restart signal on the master side is steadily on. I will look to see what might be causing this, zero in with different diagnostic signals and get back to testing. My first change didn't alter things, so I have disabled the timeout, meaning that only a hamming/CRC error will trigger the restart condition.
Final tweak on the virtual 1442 functionality
I had encountered situations where a timing vulnerability allowed the FPGA side to send the XIO Control to the Python program requestng it start reading or punching, even though we had emptied the file and it was going to be closed. To protect against this, I explicitly check for the empty string that will be returned from the file read and if I see it, I warn the user and exit.
This is a benign warning, in that the DSW will soon show that the card reader is not ready. I think this only happens on programs that insist on issuing additional reads after they received a Last Card interrupt. This modification gives the program a graceful behavior whenever this race hazard allows a read to slip through.
The behavior that triggered this vulnerability has to do with the special 'last card' mode of the 1442. If the reader is Not Ready because the last card has been fed in from the hopper, the user can push Start to trigger Last Card. The machine becomes ready and lets the user issue a read. That leaves the last card in the pre-punch buffer, where it can be removed by NPRO. However, the diagnostic program documentation mentions issuing a feed to clear the last card after it has been read, which was what had triggered the flaw before.
Design work on mirror 1442 variant functionality
I intend to have the SAC Interface Box offer two styles of 1442 support - virtual and mirror. Mirror is used with a physical 1442 that is actually handling cards, allowing the mirror adapter to capture an image of the cards in a PC file as they are read and/or punched. Mirror adapters work by shadowing the real adapter logic in the 1131, snagging the data from various XIOs and cycle steals.
The key differences are that no control operations are performed - no interrupt requests, no data gated into memory for XIO Read, XIO Sense ILSW or XIO Sense DSW functions. There is no reason to load the pre-read buffer with data, it should be spaces (all zero contents). Read versus write logic is reversed for reading cards. At the XIO Read, after I snag the word transmitted by the 1442 adapter, I need to write that into the pre-read buffer.
I won't capture NPRO automatically, the operator must push my NPRO button once it is done for the physical 1442. My main feed cycle loop won't be driven by timers, it will wait for the real 1442 to do its thing. I will have a race hazard in that the Python program must check for the dummy XIO IW and fetch the data from pre-punch before the next physical feed action occurs on the real 1442, otherwise the data will be overwritten.
I am struggling to figure out how to autodetect which mode the system desires.Since the power-up of the 1442 leaves it not-read and requiring an NPRO to flush out any cards that may have been inside, I know that the Not Ready but (bit 15) of the DSW will be on, but the only way to view that is for something to issue an XIO Sense Device for the card reader, something I can't control
However, I can hijack unused bits in the XIO Control modifier word, such that if an XIO Control with that value is issued, it puts the machine into mirror mode. To compete this, I can create a boot card for the 1442 that issues the special XIO Control. Thus, when powered up, the SAC Interface box is expecting to act as the 1442 adapter, but if it sees my special instruction it reconfigures itself for the remainder of the power-on time to work as a mirror device.
My big challenge is that I don't have a fully functioning 1442 to test out the mirror mode. I have the adapter logic, permitting me to test what happens with my special boot card and the unused control bit. I can't feed, read or punch cards until the physical 1442 is restored.
Implementing virtual 1403 printer
I structured my plan for handling the virtual 1403 printer - using buffer memories much like I did with the 1442. The 1403 has two independent mechanisms - printing and carriage movement - which are modeled with fidelity to the timing of the physical printer on an 1130.
The user initiates an XIO Init Write to print, causing the 1403 to fetch up to 60 words from core containing two print columns per word of contents. When the fetch is done, the printer issues an interrupt on IL4 indicating transfer complete. It then prints those characters on the line some milliseconds later and issues another interrupt indicating print complete.
The carriage is either spaced on line or commanded to skip until a match is found on the carriage control tape, a tape which moves line by line in synchronization with the carriage (paper). To space, the user issues XIO Control. To skip, the user issues XIO Write but instead of the address part of that IOCC pointing to a word in core, it has bits 4 to 15 set to indicate a match on channels 1 to 12 respectively of the carriage control tape.
The printer moves the paper until the selected channel has a hole in the carriage control tape, when it stops. If the printer is handling an XIO IW and has not finished printing, the skip or space is held and performed once the print complete is issued for the printing operation.
My Python program will keep the printer Not Ready until an output file is opened. When the XIO IW is issued, it is reflected to the Python program which will fetch the print data. It pulls the first word from the WCA address, which is a count of words to be printed, and then fetches that many words from core to create the output line The program triggers the transer complete interrupt to the fpga, which then handles the remainder of the 'print operation'.
Once the fpga is triggered to issue transfer complete, it waits for 200ms before triggering the print complete interrupt. This corresponds to 300 LPM which is a fair but possibly a bit high rate for the faster version to actually run (nominal rate is 600LPM but all depends on character sequence for how many times the chain has to rotate). I will treat a single space as essentially zero time because its delay is built into the timing for the line print.
The XIO Control (space a line) and XIO Write (skip a line) commands are reflected to the Python program. The Python program will have a mirror of the carriage control tape that is also held in the fpga, so that the Python program can advance the output file by the appropriate number of lines independent of the action in the fpga.
In the fpga, no delay is modeled for XIO Control but for XIO Write, timing is simulated to match how long the carriage would move at its rate of 16ms per line while skipping. Once the delay (if any) is over, the fpga issues a carriage complete interrupt on IL4. The carriage is marked busy in the DSW from when the XIO Control or XIO Write is issued until the completion of the delay but also until the Python program has picked up that XIO reflection, to prevent losing any of those carriage operations.
The fpga has to maintain a carriage control tape image, pushed down by the Python program when it makes the printer 'ready'. This has 12 bits (one per channel) and a depth of 66 lines (to match an 11" form length. The DSW always reflects whether there is are 1s in channels 9 and 12, for the current line position of the carriage. A skip to channel 1 resets the line position to 1 otherwise it advances modulo 66.
Simulating a carriage skip involves waiting 16 ms per line and advancing the line position, while a space is just one such delay and advance. If the bit from the XIO Write matches a 1 in the tape buffer at the current position, we end the skip.
I will reuse logic from the push special data functions for the 1442, which pushes in groups of 10 words. This will load my 66 word memory for the carriage control tape. Since I don't foresee any design challenges, implementation is just a straight slog through VHDL and Python and then lots of debugging.
Improvement to mirror 1132 printer function
The 1132 can leverage ideas from the 1403 and 1442 logic I created to do a better job than the current implementation. Carriage control is very similar to the 1403. Printing is quite a bit different however.
The 1132 prints by starting an operation with an XIO Control command. This causes the physical printer to begin issuing interrupts on IL1. The printer has rotating print wheels, 48 characters are placed around the wheel, with a circuit that knows what character is rotating into position next. As a character comes into position, the printer issues the interrupt and waits for the program to issue an XIO Read.
The output of the XIO Read is the bit pattern of the print character that the wheels can print right now. It is up to the program to scan through the print line they want to print and determine the column numbers for every occurrence of this one print character that is ready on the print wheels.
The program sets up words beginning in a special fixed location (0x0020) in core, with a bit for each print column. Since the printer has 120 columns and we have 16 bits in a word, we need eight words, with the last word only partially used. The final bit (15) of the last word is set to 1 by the program and tested by the printer hardware. If that bit is not 1, then the printer flags a print error.
This is an interlock to ensure that the software has finished looking up all the characters in a print line and setting the bits in the words in 0x0020-0x0027 - the program sets that bit to 0 as soon as it gets the IL1 interrupt for a new character on the print wheels, then flips it on after it has completed all 120 columns. If the program doesn't get done with its work, the bit will be off and the printer detects this 'overrun' problem.
The printer uses cycle steal to fetch the eight words from location 0x0020 to 0x0027 for each position on the print wheel - about once per 11.2 ms - having first issued the IL1 to let the program reach the upcoming character value and set up the bits in core.
Our mirror drive simply watches the XIO and cycle steal operations take place, capturing the data that flows to or from core during each. Thus, the mirror adapter will have seen the upcoming print character which it captured during an XIO Read. It will see the contents of locations 0x0020 to 0x0027 by watching the cycle steal fetches as they were issued.
I can easily loop through the print buffer memory and store the latched character value for any column where the fetched word bits were 1. This builds up the characters in the print line just as they are formed on paper as a series of hammer firings for as the wheels rotate through the characters.
When I see the XIO Control wherein the program tells the 1132 to stop sending us the IL1 interrupts - thus a stop print - the reflection of that to the Python program causes it to fetch the print buffer which will have two locations per word so a total of 6 groups of 10 words each delivers the entire print line to the program. The Python code translates from 1132 code to ASCII and writes it to the output file.
Stop printer (XIO Control) is issued after printing, spacing and skipping are done,. This is reflected to the Python program. I can see that the space or skip has completed by a bit in the sensed DSW whose value I latch. I also will see if the printer didn't see the last bit as 1, causing a print scan check, so that I can behave appropriately.
It will be essential that the Python program have a mirror image of the physical carriage control tape installed on the physical 1132, since we are going to determine how many blank lines to insert in the output file by following the physical print line number with a virtual print line number in our program. We start at 1, reset to one when skipping to channel 1, and otherwise we increment this based on space operations or the lines skipped over our mirror carriage tape. I will use the fetch special data transaction to grab the print buffer, otherwise I just watch the XIO reflections to follow along.
This requires quite a bit of rip and replace, no design challenges but more slogging though VHDL and Python just to build it. I will spend time on these coding projects over the coming days.