Unit 7.4 -- Array Control Blocks
PROG
move stuff around a two-dimensional array
PED
to learn how to pass arrays to subroutines using control
blocks
to see an example where main program calls sub1 which calls
sub2
CONCEPTS
A two-dimensional array can be passed by sending the address
of an array control block that contains:
a. pointer to data or allocated storage
b. number of rows
c. number of columns
d. first subscript (optional)
e. second subscript (optional)
(Note that an array control block is the same as a parameter
control block except that an array control block usually
contains the above information about the array. The address
of this array control block is then passed to a parameter
control block.)
A useful subroutine to have will
return the address of the array's element that is
specified by
the subscripts stored in the ACB for that array (the
subscripts
described in parts d and e above).
BE CAREFUL of registers containing return addresses or
addresses of control blocks when calling subroutines inside
subroutines!
You can designate "label" to hold the address of a block of
storage by writing
label DC A(block)
block EQU *
DC's defining storage for "block"
This technique is extremely inefficient when elements of the
two-dimensional array are processed in two-dimensional
order.
28.html
Unit 7.3 and 7.4
Units 7.3 and 7.4 lead up to the way to pass arrays to
subroutines.
Unit 7.3 is an example of a routine that will take a two-
dimensional array, a value of I and a value of J. It will
return the address of A(I,J) in a register. This can be
used to fetch the value of A[i,J] or to store something in
A[I,J].
Unlike, earlier examples, this will allow us to process
A[I,J] where I and J are not constants. This technique
should only be used where we don't have a regular pattern to
our subscript-access, such as by rows or columns as we
learned earlier in units 24 through 26.
In order to compute the address of A[i,j], we need to
1. multiply I times the number of columns in a row.
2. We then add J to this, giving us the number of integers
between the desired item and the beginning of the
array.
3. This value is multiplied by four to give us the number
of bytes between the desired item and the beginning of
the array.
4. We add the initial address of A to this to give us our
final result.
As you can see we need to do a multiplication in step 2. We
write a subroutine for this, containing the code to do "dumb
multiplication" as discussed in Unit 14.
This is illustrated (and a test program written) in Unit
7.3. Let's look at this briefly before going onto the main
routine in Unit 7.4.
The main program is precisely the same as for Unit 7.3, only
the word "MULT" is substituted for "BLAH" as we now multiply
where we added before.
Thus the only difference is the subroutine, MULT, appearing
from lines 81 to 102. In lines 83 and 84, the value of the
first thing to multiply is extracted from the parameter
control block and dumped into MULTA. In lines 87 to 88, the
value of the second thing to multiply is extracted from the
parameter control block. It is put into MULTB. Lines 88 to
96 simply perform multiplication by successive addition as
discussed in Unit 5.2.
In lines 97 to 98, we simply exercise the store value into
"var" parameter template to get the result of the
multiplication into the third parameter.
We now proceed to the subroutine that does the array
subscript calculation. There are three routines:
a. a main routine that tests everything. It performs the
computation, A[3,3]:=A[2,1]. It calls "SUBS"
b. a routine called "SUBS" that does the calculations--
i.e., determine the address given the subroutine. It
calls the MULT ROUTINE
c. the MULT subroutine that we saw in the last unit--
copied verbatim.
The SUBS is called with an "ACB" for "array control block."
This contains the information necessary to perform all sorts
of array processing. This subroutine finds the address of a
given element, but a routine could use the same information
to:
a) zero a row
b) zero a column
c) interchange rows and columns
The ACB's address will be contained in register one. The
ACB will contain:
a) the address of the block of storage (which may be a bunch
of constants defined with DC's) that is the array itself.
b) the number of rows (NOROWS)
c) the number of columns (NOCOLS)
d) the first subscript "I in A[I,J]" (ROWNO)
e) the second subscript "J in A[I,J]" (COLNO)
(Note, we are assuming that the array's ranges start at
zero, i.e., it is dimensioned as A[0..NOROWS-1,0..NOCOLS-1].
One could write these routines to handle arrays with
arbitrary ranges but that would just complicate the issue.)
The last two items won't be used for all applications. The
first two items are constants, since once the array is
allocated, its address and final ranges are permanent and do
not change.
In our program, our ACB is stored at lines 38 to 43. The
address field is predefined. Set by DC A(A) to be the
address of the block of storage containing sixteen numbers
that constitutes the data of the array. The Number of rows
is the next field, predefined to be four. After this is the
number of columns. The next two fields, for the two
subscripts, do not contain meaningful informaton at startup.
They will be loaded with the appropriate values each time we
need the address of a new element.
The procedure will put the address of the given element into
register two, i.e., the location where A[ROWNO,COLNO] can be
found, taking into account the array's physical location
(item a in the ACB), the number of rows, the number of
columns as well as the two subscripts.
(If you will, one can look at SUBS as a function. All a
function is is a procedure that puts some value in a
predefined place (the return value).
Going back to our main routine, in order to compute
A[3,3]:=A[2,1], we must
a. find out the address of A[2,1]
b. fetch the value at the address, saving it somewhere
c. find out the address of A[3,3]
d. store the value fetched in b at the address determined
in c.
Step a is done in lines 22 to 27. Lines 22 to 25 load up
the ROWNO and COLNO (in the ACB) with 2 and 1 respectively.
The address of the ACB is loaded in register 26 and we call
the subroutine in line 27.
At this point, we have the addrress of A[2,1] in register
two. We fetch the value of A[2,1] in line 28, putting it
into register 10.
It is now time to step c, relating to A[3,3]. We load 3
into both ROWNO and COLNO in lines 29 to 32. The address of
the ACB is loaded into register one at lines 33 and the
subroutine is called in lines 33.
Upon return from this subroutine, register two will contain
the address of A[2,1]. Finally, line 35 stores the value of
A[2,1], previously saved in register ten, into A[3,3] whose
address is now in register two.
Now look at our subscript routine which is located on lines
50 to 73. Our suscript routine takes an ACB and computes
the value
Address of A + (I*number of columns+J)*4
Our routine receives the address of the ACB in register 1.
This ACB contains all the values needed to compute the above
expression.
Address of A can be found in the first word of the ACB
0(0,1).
I is the first subscript, or ROWNO, and can be found in the
fourth word of the ACB or 12(0,1).
The "number of columns" can be found in the second word of
the ACB 8(0,1).
J is the second subscript which can be found in the fith
word of the ACB or 16(0,1).
The multiplication of I*number of columns is performed with
the multiplication routine. The PCB for this is found in
line 74.
Note that our main program is passing to us in register one,
the address of the ACB. In register five, we have the
return address--where we go in the main program when we are
finished with SUBS. This is what was loaded by BAL 5,SUBS.
Unfortunately, our MULT routine also expects in register one
the address of its parameter control block. Our MULT
routine also expects its return address to be put into
register five. There is an obvious conflict here. This is
resovled by loading the register one and the register five
values into temporary locations in lines 51 to 52. We
restore these as needed after return from the MULT
subroutine.
Now, we are ready to compute our expression. We first
perform the multiplication of "I*number of columns" In line
54, we load up the number of columns, located in the third
position off the ACB. It is stored into the first position
of the PCB in line 55. Then we load up I, stored in the
fourth position off the ACB. This is done in line 56. It
is put into the second position of the PCB in line 57. And
now, we load up some place to put the result of this
multiplication. SUBSTEMP will contain this value
temporarily. It is put into the third position of the PCB
for MULT in lines 58 to 59. Finally, we put the address of
the PCB into register one and call the "MULT" subroutine in
line 61.
We now need to add "J" to the result of this multiplication.
This is done in lines 62 to 64. Remember, we have to
restore register one to point to the ACB which is used by
this subroutine, rather than the PCB used by MULT.
This is multiplied by four. We could do this using another
call to the MULT subroutine, but it is far easier for both
us and the computer, if we just double the register twice
with our "AR" trick as seen in lines 65 and 66.
We add the address of A in line 67.
Finally, we restore register five, containing the place to
go home to and return in lines 68 and 69.
Lines 80 through 101 contain the MULT subroutine, again just
copied from our previous example.