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.