RCD RCD RESTART AFTER ERROR SAP10003 LXD B2,1 X SAP10004 LXD B3,2 X SAP10005
These first three lines are a re-entry point provided to allow restarting RDBCD after a card error, such as failure to feed a card, a jammed card, etc. As we will see, when such an error occurs, the program halts to allow the operator to fix whatever problem exists. After correcting the problem, when the operator restarts the program, the computer will jump to the instruction labeled RCD above. It selected the card reader for input, reloads index registers 1 and 2 with saved values, and then drops into the main body of RDBCD.
RDBCD CPY L COPY 9 LEFT ROW SAP10006 TXL B1 X SAP10007 TRA 2,4 END OF FILE EXIT SAP10008
This is the real beginning of the card reading subroutine of UA-SAP. It is assumed that the card reader has already been selected for input, so the first instruction is CPY L, which attempts to retrieve the first word of data from the card readerand store it in variable L. If the CPY succeeds, control is returned to the first instruction after the CPY instruction, which is just an unconditional branch to the rest of the subroutine. If the CPY fails because there are no more cards to read (an end of file condition), then control returns to the second instruction after the copy, which just immediately returns to the caller of RDBCD. It returns to the second word after the TSX that was used to call RDBCD. The calling code will arrange for an instruction to be there that handles the end of file condition. The first word after the subroutine call is an argument word used by subroutine C1. The third word after the subroutine call is the return location that indicates the successful reading of one card into the supplied 12-word line buffer.
B1 STQ LS SET LEFT SUM SAP10009 SXD B2,1 SAVE INDEX REGISTERS SAP10010 SXD B3,2 X SAP10011 LXD B4,1 SET DIGIT ROW COUNT SAP10012 CPY R COPY 9 RIGHT ROW AND SAP10013 STQ RS SET RIGHT SUM SAP10014
The word just read from the card reader is the left half of row 9 of a card. It was read into variable L, but it is also stored in variable LS, which is an input to subroutine C1. Index registers 1 and 2 are saved, as they will be used in RDBCD. They will be restored before RDBCD returns to its caller. Then the decrement field of the instruction labeled B4, which contains the number 8, is loaded into index register 1, which will be used to countdown the rows as they are read in. Lastly, the right half of row 9 is read from the card reader with the CPY R instruction and also stored in variable RS, which is another input to C1. Row 9 has thus been stored in both the variable pairs L/R and LS/RS.
TSX C1,2 ENTER CONVERSION LOOP SAP10015 B2 TXL B5 LEAVE CONVERSION LOOP SAP10016 ALS 1 SAP10017 B3 TXL C2 INITIALIZE BCD RECORD SAP10018
After reading both words for row 9 from the card reader, subroutine C1 is called to spread out the bits into the line buffer (the address of which is provided by the caller of RDBCD). the line labled B2 is the normal return of C1. The next two lines are the callback routine. This callback routine shifts A left by one bit before jumping to label C2, which is the entry to subroutine C1 that just stores A into the appropriate word of the line buffer. This serves to initialize the line buffer. Normally, we would expect the variables LS and RS to contains an accumulated logical sum (OR) of all of the cards rows. By repeatedly adding this accumulated logical sum to itself in each character position of the line buffer, we can generate the digit and character encodings. Because row 8 will be handled specially, the callback here goes ahead and multiplies the bits coming out of the row 9 by 2, to account for the accumulation of these row 9 bits for both row 9 and row 8.
Note that it is assumed that the CPY instruction for the right half must succeed. There is no provision made for failure. That is, there are no instructions in the second and third words after the CPY that could handle an error result.
B5 CPY 8L COPY 8 ROW AND SAP10019 STQ LS USE AS SUM SAP10020 CPY 8R X SAP10021 STQ RS X SAP10022 TSX C1,2 ENTER CONVERSION LOOP SAP10023 B4 TXL B6,0,8 LEAVE CONVERSION LOOP SAP10024 ALS 3 ADD 8 TIMES 8 ROW SAP10025 TXL C3 X SAP10026
CPY instructions for row 8 are executed. The data words are read from the card reader into the variable pair 8L/8R and also stored in LS and RS for use by subroutine C1. Subroutine C1 is called. The callback routine immediately adds the full value of the 8 row to the line buffer by shifting A to the left three positions and then branching to the instruction labeled C3 (so each set bit in the 8 row contributes the value 8 to the corresponding character position).
B6 CAL L USE 9 ROW AS SUM SAP10027 SLW LS X SAP10028 CAL R X SAP10029 SLW RS X SAP10030
Having taken care of the special 8 row, the 9 row is retrieved and placed in the variable pair LS/RS to begin building the cumulative logical sum of the digit rows.
B13 TXL B7,1,1 TEST FOR ZERO ROW SAP10031 B14 CPY L COPY LEFT ROW AND SAP10032 TXL B8 TEST FOR END OF RECORD SAP10033 B17 HTR RCD ERROR SAP10034 TXL B9 END OF RECORD SAP10035
The value of index register 1 is compared to 1. If equal (or less), then row 1 has already been processed so this must be row 0. Index register 1 will also contain the value 1 if we are reading the other zone rows (rows 11 and 12), but in that case, the instruction labeled B13 will never be executed. Instead control will jump directly to B14 for those rows, as we will see below.
Assuming this is not row 0, the left word of the row is retrieved into variable L by the CPY instruction. If the CPY is successful, the unconditional branch to B8 is taken. If the card has already been completely read, there is an end of record condition and control passes to the instruction in the third word after the CPY, which is an unconditional branch to B9. Any other error will transfer control to the instruction at B17. That instruction is HTR, which halts the computer, after placing its operand in the program counter. The computer operator should resolve the error and restart the computer. Upon restart, control will be tranferred to the restart code labeled RCD, as explained above.
B8 CAL L TEST LEFT ROW FOR SAP10036 ANA LS ILLEGAL DOUBLE PUNCH SAP10037 TNZ B17 X SAP10038 B10 CAL L FORM LOGICAL SUM SAP10039 ORS LS OF LEFT ROWS SAP10040
Now the code checks for an illegal double punch by ANDing the (left half of the) row just read with the accumulated logical sum so far. If the result is not zero, it means that two rows were punched in some column, which is illegal if one of those rows is not the row 8. This is why row 8 was handled specially above and the row 8 data was not included in the logical sum. In case of an illegal double punch, the branch to B17 is taken, which is the code above that halts the computer to allow the operator to resolve an error. If there is no illegal double punch, the logical sum is updated by ORing the data from the current row into the logical sum. The ORS instruction used here stores the result of the OR operation into the operand location.
CPY R COPY RIGHT ROW AND SAP10041 CAL R TEST FOR ILLEGAL SAP10042 ANA RS DOUBLE PUNCH SAP10043 TNZ B17 X SAP10044 B11 CAL R FORM LOGICAL SUM OF SAP10045 ORS RS RIGHT ROWS SAP10046
This code for the right half row is almost identical to that above for the left half row. The only difference is that it is assumed that no error condition can occur when executing the CPY for the right half.
TNX B12,1,1 TEST FOR ZONE ROWS SAP10047 TSX C1,2 ENTER CONVERSION LOOP SAP10048 TXL B13 LEAVE CONVERSION LOOP SAP10049 TXL C3 ADD TO BCD RECORD SAP10050
A check is made to see if the current row is a zone row (row 11 or 12). This check is made by conparing the value of index register 1 to 1. Remember that the value is greater than one for the digit rows. If the current row is a zone row, the branch to B12 is taken. Otherwise, index register 1 is decremented and then subroutine C1 is called. Here, which is the code path used for rows 7 through 1, and 11 and 12, the callback routine does nothing except return to C3, which adds the bits in A to the corresponding characters in the line buffer.
B7 CAL 8L ADD 8 LEFT ROW TO SAP10051 ORA LS LEFT LOGICAL SUM SAP10052 SLW LDS X SAP10053
This code, at label B7, is the destination of the branch above when the current row is row 0. Now, working on the left half, the data from row 8 is retrieved and ORDed into the accumulated logical sum and the result, the accumulated OR of all digit rows from 9 to 1, is stored in the variable LDS.
CPY LZ COPY ZERO LEFT AND SAP10054 ANA LZ FORM INDICATOR FOR SAP10055 SLW LS BOTH DIGIT AND ZERO SAP10056
Now the data for the left half of row 0 is read from the card reader into variable LZ. It is also ANDed with the left digit sum, LDS, which is still in A to form a word which contains bits corresponding to every column in the left side of the card in which there is both a punch in a digit row and a punch in the row 0. This is true for 11 different valid character, but not true for the digit zero. Columns containing only a zero punch, representing the digit zero, must be excluded here because the digit zero is encoded by the six bit sequence 000000, so bits corresponding to a punch in the row 0 without an additional punch in a digit row cannot be allowed to contribute to the value of any characters.
This logical product of row 0 with the accumulated logical sum of the digit rows is stored in LS, eliminating its previous value. Therefore, the accumulated logical sum is being restarted for the zone rows, with the initial value having only those bits set which correspond to columns which have both a zero punch and some digit row punch.
CAL 8R ADD 8 RIGHT ROW TO SAP10057 ORA RS RIGHT LOGICAL SUM SAP10058 SLW RDS X SAP10059 CPY RZ COPY ZERO RIGHT AND SAP10060 ANA RZ FORM INDICATOR FOR SAP10061 SLW RS BOTH DIGIT AND ZERO SAP10062
Now the right half of row 0 is handled identically to the left half.
B12 TSX C1,2 ENTER CONVERSION LOOP SAP10063 TXL B14 LEAVE CONVERSION LOOP SAP10064 ALS 4 SHIFT TO ZONE POSITION SAP10065 TXL C3 X SAP10066
This is the call to subroutine C1 for all zone rows, including row 0. It is very similar to that for the digit rows. The only difference is that the callback routine shifts the bits to the left by four positions so that they accumulate in the high two bits of each character in the line buffer. After subroutine C1 returns, an unconditional branch to B14 is taken, which as mentioned above is the first instruction in the sequence of code that reads and processes rows 11 and 12.
B9 CAL LS SAVE LEFT ZONE SUM SAP10067 SLW L X SAP10068 CAL LDS FORM INDICATOR FOR SAP10069 COM ZERO AND X AND / OR Y SAP10070 ANA LZ IN LEFT ROWS SAP10071 ANS LS X SAP10072
Control is transferred to B9 at the end of a record. That is, after all rows on a card have been read. At this point, most characters in the line buffer are complete. However, there are a few special cases that need to be cleaned up. First, there are two characters composed only of double punches in zone rows. One is a formed by a 0-11 punch and the other is formed by a 0-12 punch. These characters cannot be printed by the IBM 704’s line printer, but they are used as special marks on tape. We would not expect to see them in assembly source code, but the subroutine RDBCD has been written for more general purposes and takes care to handle these characters properly.
These characters are recognized by ANDing the complement of the digit sum with row 0 and the logical sum of the zone rows. This yields a word in which bits set indicate that the corresponding column of the card was punched in row 0 and at least one other zone row, while not being punched in any digit row. Although the comments attached to the code above indicate that a 0-11-12 punch could be a valid character here, an 11-12 double punch would actually be caught as an illegal double punch in the code above between labels B8 and B11.
CAL RS SAVE RIGHT ZONE SUM SAP10073 SLW R X SAP10074 CAL RDS FORM INDICATOR FOR SAP10075 COM ZERO AND X AND/OR Y SAP10076 ANA RZ IN RIGHT ROWS SAP10077 ANS RS X SAP10078
The equivalent bit product is produced for the right half of the card.
TSX C1,2 ENTER CONVERSION LOOP SAP10079 TXL B15 LEAVE CONVERSION LOOP SAP10080 SLW T MULTIPLY INDICATOR SAP10081 ALS 2 BITS BY TEN SAP10082 ACL T X SAP10083 ALS 1 X SAP10084 TXL C3 X SAP10085
Subroutine C1 is then called with a callback routine that multiplies each set bit in A by 10, adding the result into the line buffer. This produces these two special characters which have encodings of 011010 and 101010 (binary). It does this by storing the original word in the temporary variable T, shifting to multiply it by four, adding the original value back in, and shifting again to multiply by two, producing the product 2 * (4 + 1) = 10.
B15 CAL LDS FORM INDICATOR FOR SAP10086 SWT 2 SAP10087 WTD 1 SAP10088 ORA LZ BLANK COLUMNS IN SAP10089 ORA L LEFT HALF OF CARD SAP10090 COM X SAP10091 SLW LS X SAP10092
The last thing to do is to properly handle blank columns, which should be interpreted as spaces in the input. Spaces are encoded as 110000, but at this point, all characters in the line buffer corresponding to blank columns have the value zero, because they never had any bits set in the words read from the card reader. The code above forms the logical sum of all rows of the card by ORing together the accumulated bits of rows 9 to 1 (LDS), row 0 (LZ), and rows 11 and 12 (L). The complement of this word yields bits set corresponding to every column that had no punch at all.
In the middle of doing this, the code prepares to write the line buffer to tape by first executing an SWT 2 instuction to see if the operator is requesting the card input to be written to tape. If sense switch 2 is up (closed), the instruction WTD 2 is executed, which selected tape drive 2 for writing. If sense switch 2 is down (open), the WTD instruction is skipped.
CAL RDS FORM INDICATOR FOR SAP10093 ORA RZ BLANK COLUMNS IN SAP10094 ORA R RIGHT HALF OF CARD SAP10095 COM X SAP10096 SLW RS X SAP10097
The indicator for blank columns is now formed for the right half of the card just as it was for the left half above.
TSX C1,2 ENTER CONVERSION LOOP SAP10098 TXL B16 LEAVE CONVERSION LOOP SAP10099 SLW T MULTIPLY INDICATOR SAP10100 ALS 1 BITS BY 3 AND SAP10101 ACL T SHIFT TO ZONE POSITION SAP10102 ALS 4 X SAP10103 TXL C3 X SAP10104
Subroutine C1 is called with a callback routine that produce 11 in the two high bits of every character in the line buffer that corresponds to a blank column on the card, thus producing the encoding of the space character as desired.
B16 LXD B2,1 RESTORE INDEX REGISTERS SAP10105 LXD B3,2 AND RETURN TO MAIN SAP10106 TRA 3,4 PROGRAM SAP10107
At this point, the input line buffer is complete. Index registers are restored and RDBCD returns to its caller at the third word after the subroutine call, which is the return for successful reading of one line without an end of file condition.
This concludes the presentation of the card reading subprogram of the assembler UA-SAP for the IBM 704 computer. Just as last time, I leave by giving the code unbroken in its original format:
RCD RCD RESTART AFTER ERROR SAP10003 LXD B2,1 X SAP10004 LXD B3,2 X SAP10005 RDBCD CPY L COPY 9 LEFT ROW SAP10006 TXL B1 X SAP10007 TRA 2,4 END OF FILE EXIT SAP10008 B1 STQ LS SET LEFT SUM SAP10009 SXD B2,1 SAVE INDEX REGISTERS SAP10010 SXD B3,2 X SAP10011 LXD B4,1 SET DIGIT ROW COUNT SAP10012 CPY R COPY 9 RIGHT ROW AND SAP10013 STQ RS SET RIGHT SUM SAP10014 TSX C1,2 ENTER CONVERSION LOOP SAP10015 B2 TXL B5 LEAVE CONVERSION LOOP SAP10016 ALS 1 SAP10017 B3 TXL C2 INITIALIZE BCD RECORD SAP10018 B5 CPY 8L COPY 8 ROW AND SAP10019 STQ LS USE AS SUM SAP10020 CPY 8R X SAP10021 STQ RS X SAP10022 TSX C1,2 ENTER CONVERSION LOOP SAP10023 B4 TXL B6,0,8 LEAVE CONVERSION LOOP SAP10024 ALS 3 ADD 8 TIMES 8 ROW SAP10025 TXL C3 X SAP10026 B6 CAL L USE 9 ROW AS SUM SAP10027 SLW LS X SAP10028 CAL R X SAP10029 SLW RS X SAP10030 B13 TXL B7,1,1 TEST FOR ZERO ROW SAP10031 B14 CPY L COPY LEFT ROW AND SAP10032 TXL B8 TEST FOR END OF RECORD SAP10033 B17 HTR RCD ERROR SAP10034 TXL B9 END OF RECORD SAP10035 B8 CAL L TEST LEFT ROW FOR SAP10036 ANA LS ILLEGAL DOUBLE PUNCH SAP10037 TNZ B17 X SAP10038 B10 CAL L FORM LOGICAL SUM SAP10039 ORS LS OF LEFT ROWS SAP10040 CPY R COPY RIGHT ROW AND SAP10041 CAL R TEST FOR ILLEGAL SAP10042 ANA RS DOUBLE PUNCH SAP10043 TNZ B17 X SAP10044 B11 CAL R FORM LOGICAL SUM OF SAP10045 ORS RS RIGHT ROWS SAP10046 TNX B12,1,1 TEST FOR ZONE ROWS SAP10047 TSX C1,2 ENTER CONVERSION LOOP SAP10048 TXL B13 LEAVE CONVERSION LOOP SAP10049 TXL C3 ADD TO BCD RECORD SAP10050 B7 CAL 8L ADD 8 LEFT ROW TO SAP10051 ORA LS LEFT LOGICAL SUM SAP10052 SLW LDS X SAP10053 CPY LZ COPY ZERO LEFT AND SAP10054 ANA LZ FORM INDICATOR FOR SAP10055 SLW LS BOTH DIGIT AND ZERO SAP10056 CAL 8R ADD 8 RIGHT ROW TO SAP10057 ORA RS RIGHT LOGICAL SUM SAP10058 SLW RDS X SAP10059 CPY RZ COPY ZERO RIGHT AND SAP10060 ANA RZ FORM INDICATOR FOR SAP10061 SLW RS BOTH DIGIT AND ZERO SAP10062 B12 TSX C1,2 ENTER CONVERSION LOOP SAP10063 TXL B14 LEAVE CONVERSION LOOP SAP10064 ALS 4 SHIFT TO ZONE POSITION SAP10065 TXL C3 X SAP10066 B9 CAL LS SAVE LEFT ZONE SUM SAP10067 SLW L X SAP10068 CAL LDS FORM INDICATOR FOR SAP10069 COM ZERO AND X AND / OR Y SAP10070 ANA LZ IN LEFT ROWS SAP10071 ANS LS X SAP10072 CAL RS SAVE RIGHT ZONE SUM SAP10073 SLW R X SAP10074 CAL RDS FORM INDICATOR FOR SAP10075 COM ZERO AND X AND/OR Y SAP10076 ANA RZ IN RIGHT ROWS SAP10077 ANS RS X SAP10078 TSX C1,2 ENTER CONVERSION LOOP SAP10079 TXL B15 LEAVE CONVERSION LOOP SAP10080 SLW T MULTIPLY INDICATOR SAP10081 ALS 2 BITS BY TEN SAP10082 ACL T X SAP10083 ALS 1 X SAP10084 TXL C3 X SAP10085 B15 CAL LDS FORM INDICATOR FOR SAP10086 SWT 2 SAP10087 WTD 1 SAP10088 ORA LZ BLANK COLUMNS IN SAP10089 ORA L LEFT HALF OF CARD SAP10090 COM X SAP10091 SLW LS X SAP10092 CAL RDS FORM INDICATOR FOR SAP10093 ORA RZ BLANK COLUMNS IN SAP10094 ORA R RIGHT HALF OF CARD SAP10095 COM X SAP10096 SLW RS X SAP10097 TSX C1,2 ENTER CONVERSION LOOP SAP10098 TXL B16 LEAVE CONVERSION LOOP SAP10099 SLW T MULTIPLY INDICATOR SAP10100 ALS 1 BITS BY 3 AND SAP10101 ACL T SHIFT TO ZONE POSITION SAP10102 ALS 4 X SAP10103 TXL C3 X SAP10104 B16 LXD B2,1 RESTORE INDEX REGISTERS SAP10105 LXD B3,2 AND RETURN TO MAIN SAP10106 TRA 3,4 PROGRAM SAP10107
— ReallyOld