CSIRAC - The First Australian Digital Computer - Examples

I have extracted a couple of demonstration programs from the CSIRAC library. As these tend to have minimal (if any) comments, I have added suitable comments. The library routines were provided to me by the University of Melbourne, Engineering and Information Technology Collection, and are distributed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0). To simplify these as examples, I have deleted the PRIMARY and CONTROL programs from the listings of the tapes, and added comments in blue. The original 12-hole tapes can be downloaded and run with the emulator.

Simple multiplication demonstration

This program reads in the setting of NA and NB switch registers, treating them as integers, it multiplies them together, and prints out the result on the printer.

printer subroutine
10T set loading address to start at 32 (1,0)
2S assign routine number 2
1S and 1 for internal references
1 0 1D SDset D1=0 (D1 = D1 - D1)
1 11A 19M C set C=100000 limit for 5-digit numbers
1 2 CAD D0 = A, A=0
1 3 15PEPDincrement return address in D15
1 4 PEPSskip next instruction
1 5 PEPAadd PE (1024) into A
1 6 C SDD0 = D0 - C repeat subtract
1 7 SDCSif sign(D0) skip to leave loop
1 81A 5K S jump to 1,5 repeating subtracts
1 9 C PDD0 = D0 + C
110 1ZASDD1 = D1 - (A==0?0:1)
111 1SDCSif D1 < 0 skip
112 31K A set A = space (31)
113 CAOTprint A, clear A
1141A 20M XBA,B = 0.1 * C
115 ZACSif A!=0 skip
116 15D S return via D15
117 CAC C = A, A = 0
1181A 6K S jump back to 1,6
119 3 1HLM 3,1,21,0 = 100000
120 119HAL 1,19,6,13 = 0.100000
1S re-assign routine number 1 for main routine
121 PST halt to allow switches to be set
122 A SAclear the A register (A = A - A)
123 NAC read the NA switches into C
124 NBXBand multiply by the NB switches
125 B A Move the B register into A
126 HAA and divide by 2
127 29K OTprint line feed
128 30K OTprint carriage return
129 15S D copy S into D15 for return
1302A K S jump into print subroutine
1311A K S jump back to halt for another tr
2 01A DK S trigger CONTROL to enter program at main routine

The main routine reads the two switch registers and forms their product. It then moves the printer to a new line, then invokes the print subroutine.

The print subroutine (1,0 to 1,20) starts by setting C to 100,000 and the number to be printed int D0. Using a loop subtracting C, and incrementing A until D0 goes negative, it determines the number of times A is greater than 100,000. Having reached that point it adds back C into D0, and tests the value of A. If A is zero it substitutes a space character, then prints the value in A. For the printer, characters '0' to '9' have values 0 to 9, so this is easy. If it was a punch routine, for which the character set is different, it would be a bit more complex.

Having printed a digit, at (1,13), it then proceeds to multiply C by 0.10000, or 52429. Multiplication can be thought of in three ways - two integers, or two fractions, or one of each. The result can then be interpreted in three corresponding ways, a double length fraction, with the decimal point between P20 and P19 of A, or a double length integer with decimal point at P1 of B, or, in the case of a mixed product, in between A and B. The latter applies here, and the value in A is the integer after C has been divided by 10.

The code then tests if the resulting value in A is zero, in which case all 6 digits have been printed (or suppressed) and the subroutine returns to the main routine using D15. Otherwise, the value in A is transferred to C and the process repeats, printing subsequent digits.

The print routine has a bug! If the value to be printed is zero, then D1 never gets set and the final zero is suppressed along with all the rest. The result is just six spaces.

The tape file to run this program in included in the Emulator Download as Multiplication.cvt. In the original library it is listed as T732.CVT.

Print a table of Tan(x)

This is a simple program that varies x from 0.05 (0.05) 0.45 printing x and tan(x).

* T725 Demonstration Tan table
* Headed table of x, angles 0(5 deg)45 deg and
* corresponding value of tan(x) to 6 dec. places
* Tan(x) = 0.785414 x
* + 0.161198 x^3
* + 0.041414 x^5
* + 0.006624 x^7
* + 0.005348 x^9
* where x is a fraction of 90 deg
* The above is what is typed on the tape header, but in fact
* it is a headed table with x ranging 0.05(0.05)0.45
* i.e angles 4.5 deg(4.5 deg)40.5 deg and the corresponding
* value of tan(x) to 6 dec. places
12T set load address = (1,2) = 34
2S subroutine to print a fraction is ref. 2
1S set local base
12 SACStest argument in A, skip if negative
13 3K PSjump to (1,7)
14 CASAA = 0 - A
15 11K OTprint '-'
16 PEPSskip next instruction
17 10K OTpunch '+'
18 12K OTpunch '.'
19 1A 17M C C = 10
110 5 K D D0 = 5*32
111 CAXBLoop: A,B = A*10, A=most significant digit as an integer
112 CAOTprint A, clear A
113 RBA A = fraction remainder
114 1 K SDD0=D0-1*32
115 SDCSskip if D0 negative
1161A 9K S loop back to Loop (1,11)
117 15PEPDincrement return address
118 15D S and return to caller
119 M P (0,0,0,10) = constant=10
 
3S subroutine to calculate tan(x)
1S set local base address
120 A PAdouble the argument A=A+A
121 A D D = A
122 A C C = A
123 CAXBA,B = A * C
124 CAC C = A
1251A 17M XBA,B = C * 0.004988 (2,5)
1261A 18M PAA = A + 0.006624 (2,6)
127 CAXBA,B = A * C
1281A 19M PAA = A + 0.041414 (2,7)
129 CAXBA,B = A * C
1301A 20M PAA = A + 0.161198 (2,8)
131 CAXBA,B = A * C
2 01A 21M PAA = A + 0.0785418 (2,9)
2 1 CAC C = A, A=0
2 2 D XBA,B = D * C
2 3 15PEPDincrement return address in D15
2 4 15D S and return to caller
2 5 2S Z (0,2,17,23) = 0.004988 (0.005?)
2 6 3R D (0,3,12,17) = 0.006624
2 7 21HAD (0,21,6,17) = 0.041414
2 8 218D OT(2,18,17,2) = 0.161198
2 9 1218A CA(12,18,4,9) = 0.785418
 
1S main routine
210 27K OTset printer to figure shift
211 828K HUH = (8,28), 8 is count,
212 6HUD D6 = H * 1024
213 29K OTLoop: print line feed
214 30K OTprint carriage return
215 6HUSDD6 = D6 - (H * 1024)
2161A 25M B B = 0.050001 (3,3)
217 6B PDD6 = D6 + B
218 6D A A = D6
219 15S D D15 = S (return address)
2202A K S Jump to print routine 2
221 6D A A = D6
222 15S D D15 = S (return address)
2233A K S Jump to Tan routine 3
224 31K OTprint space
225 31K OTditto
226 15S D D15 = S (return address)
2272A K S Jump to print routine 2
228 6HUPDD6 = D6 + H*1024
229 6SDCSif D6 < 0 skip next instruction
2301A 3K S loop back to Loop (2,13)
231 29K OTprint line feed
3 0 29K OTditto
3 1 3131K P pulse speaker
3 2 3130K PSloop back to continue hooting
3 3 25RDCA(0,25,19,7) = 0.050001
3 4 29K OTentry: print line feed
3 5 29K OTand a second line feed
3 6 30K OTprint carriage return
3 7 28K OTprint letter shift
3 8 31K OTprint space
3 9 31K OTditto
310 23K OTprint 'X'
311 31K OTprint space
312 31K OTditto
313 31K OT..
314 31K OT..
315 31K OT..
316 31K OT..
317 31K OT..
318 19K OTprint 'T'
319 K OTprint 'A'
320 13K OTprint 'N'
321 31K OTprint space
322 23K OTprint 'X'
323 29K OTprint line feed
324 30K OTprint carriage return
3251A K S jump to start of main routine
3261A26DK S trigger to enter at (3,4)

This program consists of three routines, a print subroutine, a subroutine to calculate Tan(x), and the main routine, which cycles through the values of X, printing that value, calculating its tangent, and printing that.

The previous program was mostly working with integers, this one is mostly with fractions. Printing fractions is fairly easy, print the leading '0.' then loop around 6 times. Each time round multiply the fraction by 10, and the most significant digit ends up as an integer in A. As the printer maps 0..9 to '0'..'9' just print that value. Loop around until all six digits have been printed, and we're done. The constant (0,0,0,10) is would be simply punched as that, but the listings are generated from the 12-hole binary tape and it doesn't know that this is a constant, so it maps (0,10) to (M P).

The algorithm for generating Tan X is a sequence of multiplies and adds. I have never had to devise one of these routines, simply using a library function. I cannot comment on whether this is the best algorithm or not, but it is a splendid example of condensing the algorithm into a minimal sequence of orders. On entry the argument is in A, the return address is actually the address of the jump which invokes the subroutine, but by adding P11 (1024) to it, just before the return, it takes us back to the order following that jump.

The main routine is triggered to enter at (3,4) which is in the middle of the routine. The code following this simply prints out a title, prior to jumping in to the start of the routine to set up the loop over the values. A feature of interest is that the constant (0,25,19,7) is equivalent to the fraction 0.050001 which means the values are slightly out of spec. This is because it is not possible to specify 0.050000 in 20 bits. The alternative value produced by (0,25,19,6) comes out at 0.049999, which is equally in error. It is therefore a case of using the nearest approximation and someone decided that the slightly larger value would be the best choice in this instance.

Another interesting quirk is the use of (8,28) to initialise H with. The reason is that they wanted to use D0 as both a counter, and as the value of X. By judicious use of addition and subtraction, the 8 gets incremented as the counter, and when it reaches 16, the test at (2,29) tests it as negative, and skips out of the loop. By subtracting H from D0 at (2,15) it generates a value for X, which gets incremented at (2,17), then H is added back in at (2,28). It took me a while to work out what was going on there, and this seems to be a fairly typical trick to avoid using other registers or storage locations.

The program ends by dropping out of the loop with the test at (2,29), skipping to (2,31) where a further line feed and carriage return are printed, the program drops into an endless loop pulsing the loudspeaker, which prompts the operator to operate the stop switch.

The tape file to run this program in included in the Emulator Download as TangentTable.cvt. In the original library catalogue it is listed as T725.CVT.

Interprogram examples

The emulator written for Windows98, which is available online, came with four simple programs written for the Interprogram interpreter. I include these here, again courtesy of the University of Melbourne, and under the above license conditions. The files are included in my emulator download.

Ex1.idp - A simple calculation

        (1)  TITLE  ALPHA = BETA - 27.394 + A(3).(X/Y)
   bbbbb(2)  SYMBOLS FOR INTEGERS   J
        (3)  MAXIMUM SUBSCRIPTS   A(3)
        (4)  COMPILE THE FOLLOWING INTERPROGRAM

   *1    INPUT J
   *2    IF THIS IS ZERO, GO TO *7     # END WHEN J = 0
   *3    INPUT BETA, & A(3), & X, & Y
   *4    TAKE X, DIVIDE BY Y, MULTIPLY BY A(3)
         SUBTRACT 27.394, ADD BETA, REPLACE ALPHA
         TAKE J, OUTPUT, OUTPUT ALPHA
   *6    GO TO *1
   *7    END OF INTERPROGRAM
       1   25.382    2.754      7     0.92
       2   31.542   -1.832      9     1.24
       3   22.379   -0.023     11     2.57
       4   23.456   +0.001     23     0.17
       5   27.356   +1.113      3   -95.76  
       0
   bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
   

This program reads in lines of five numbers, J, BETA, A(3), X and Y. J is simply an index, at J = 0 acts to terminate the input. The program, as stated in the first line, calculates ALPHA = BETA - 27.394 + A(3).(X/Y), and prints out J and ALPHA, then returns to read the next line.

The use of lower case 'b' is because Interprogram makes use of blank tape as part of the specification, and the emulator needs to be able to provide the relevant value as an input character to enable the interpreter to work correctly.

The first line: TITLE... requires blank tape to terminate the arbitrary string of text, and owing to the way it was written, you have to put several blanks, in this case the title include the end of line marker (carriage return for the Flexowriter, which ignores line feeds!).

Line (2) defines J to be integer, any other variables are real values. I find it interesting that the early autocodes tended to have very basic identifiers, whereas Interprogram allows fairly generous freedom (though it ignores any characters after the fourth.) Line (3) is optional and can be omitted if there are no arrays. Line (4) is required and the program will fail if it is omitted. Actually all that is needed is the last four characters 'GRAM'.

The program proper follows, with most of them being labelled in this example, although only *1 and *7 are referenced. Each statement begins with a command word, followed by a variable or constant, the use of ',' separates multiple statements on the same line, and '&' is taken to mean use the previous command again. There is a pseudo variable 'THIS' which means the result of the previously computed value. The command TAKE, sets THIS to the value specified, and the subsequent commands imply the use of THIS value to operate with the given value. OUTPUT, on its own, means 'OUTPUT THIS'.

Usual practice was to append data to the program file, as seen here, though the system would read in the program and pause to allow the operator to load a different tape if required.

Ex2.idp Simple interest calculation, scaled arithmetic

       (1)  TITLE  INTEREST CALC
  bbbbb(2)  SYMBOLS FOR INTEGERS   J
       (4)  COMPILE THE FOLLOWING INTERPROGRAM

   *1    INPUT J, & X
   *2    TAKE J, DIVIDE BY 100               #SCALED - RULE 3
   *3    MULTIPLY BY X, REPLACE INTEREST     #SCALED - RULES 2(B), 1
   *4    MULTIPLY BY 20, ADD .0001, REPLACE INTEREST, REPLACE J #INTEGER-RULE 1
         OUTPUT, SUBTRACT INTEREST           #-VE FRACTION OF SHILLING
         MULTIPLY BY -12, REPLACE J, OUTPUT   #THIS = INTEGER PENCE
   *7    END OF INTERPROGRAM
  eeeee     5  12.0000
  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
  

In this program line *2 shows an integer being converted to real (5 -> 0.05), then conversion back to integers to get shillings and pence in lines 4 and 6. For those who post-date decimal currency, I should explain that these sort of calculations were common-place with the old money!

Ex3.idp Complex Arithmetic Calculations

       (1)  TITLE  LOG(X+IY)=U+IV WHERE  U=LOG(SQRT(X*X+Y*Y)) & V=ARCTAN Y/X
  bbbbb(2)  SYMBOLS FOR INTEGERS   NONE
       (4)  COMPILE THE FOLLOWING INTERPROGRAM
  
   *1    INPUT X, & Y
   *2    TAKE X, MULTIPLY BY X, REPLACE XSQUARED
   *3    TAKE Y, MULTIPLY BY Y, ADD XSQUARED 
   *4    FORM SQUARE ROOT, FORM NATURAL LOG, REPLACE U
         TAKE Y, DIVIDE BY X, FORM ARCTAN
         IF X IS POSITIVE, GO TO *37                   # EXTEND ARCTAN
         ADD 1, IF THIS IS GREATER THAN 1, ADD -2      # TO RANGE (-1,1)
   *37   MULTIPLY BY 3.14159, REPLACE V                # V IN RADIANS
         OUTPUT U, & V
   *40   END OF INTERPROGRAM
  eeeee
       2  4     
  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
  

Modern programming languages provide constructs for calculating with complex numbers, but in the early days it all had to be explicitly coded. Use of conditional statement (IF) is illustrated on lines 6 and 7 (unlabelled)

Ex4.idp Function call and output formatting

       (1)  TITLE  EXAMPLE OF OUTPUT, LAYOUT AND FUNCTION FORMATION
  
  bbbbb(2)  SYMBOLS FOR INTEGERS   NONE
       (4)  COMPILE THE FOLLOWING INTERPROGRAM
            
            PUNCH THE FOLLOWING CHARACTERS
     DATUM      LOG     EXP(LOG)    SINE      TAN  ARCTAN(TAN)
  ------------------------------------------------------------l
  bbbbbbCOPY TAPE                  # DATA TAPE IDENTIFICATION
   *2    INPUT DATUM, GO TO *3, GO TO *4  #WHEN 'END' IS READ
   *3    OUTPUT, FORM NATURAL LOG, OUTPUT,
         FORM EXPONENTIAL, OUTPUT,        # EXP(LOG(DATUM))
         TAKE DATUM, FORM SINE, OUTPUT,
         TAKE DATUM, FORM TANGENT, OUTPUT,
         FORM ARCTAN, OUTPUT              # END PRINTED ROW
         GO TO *2                         # REPEAT FOR EACH DATUM VALUE
   *4    END OF INTERPROGRAM
  eeeeeSAMPLE SET OF DATA
  bbbbbee0.0   0.166667   0.25   0.333333   0.5   0.666667   1.0
   2.71828   -0.166667   -0.25   -0.5   123.456   -4567.89   END
  bbbbbbbbbbb
  

This routine makes use of the PUNCH command to copy headings to the output. As with TITLE, the text is terminated with blank tape. The COPY TAPE command reads some text from the data tape (in this case the text is on the same tape, following the program), again terminated by blanks. I am unclear about the use of the character 'e' - as far as either of the emulators are concerned it is completed ignored!

The data consists of a series of numbers, read in as DATUM, each number is taken as the argument to a number of standard functions. Note also the use of optional returns from the INPUT command, where 'END' is recognised as a terminator.