Unit 9.1 -- Base Registers

Program

Move some numbers around as indicated

PED

To learn about Base Register Usage

Concepts

Implicit address (label) --- A,B,IF001
Explicit address --- 0(0,1)  4(2,12)
Form is offset(index_#,base_#)
Limit on offset is 4096

An implicit address in an RX instruction is converted to an
explicit address

The base register used is selected from those appearing in
the second operand of the USING statement.

Offset is address of label - address of first operand of
USING statements

Where multiple base registers are provided, one is chosen so
as to ensure that offset is 4095 or less

Addressability error--no such base register found

     To deal with programs more than 4096 bytes

loc1 - some address still addressable (original base
register or another address done this way)

loc2 - some address not addressable

lab1 - a label at loc1

Put at loc2
length1 equ *-lab1

In the code, we write
   LA regb,lab1
   A  regb,=a(length1)
   USING loc2,regb


SF

     USING     *,regA
     USING     label,regA

indicates that regA is assumed to contain the address of
  next instruction
  label

     BALR regA,0

loads regA with address of next instruction.  Doesn't branch
anywhere.  Often used in conjunction with "USING *,regA"

LTORG

causes literals to be dumped into memory at current
location.  Used to ensure that there is no addressability
error concerning the literals.

62.html

                    Unit 9.1 Base Registers

In the past sections, we have been including the code:

         BALR  12,0
         USING *,12

with little or no explanation of what these lines mean.

There  was  a  brief  mention that  these  two  instructions
establish   a   "base   register,"   that   they   establish
"addressability."

We  will  learn  what these instructions do and  what  these
concepts mean.  We will also learn that some times, we  need
to  have  more than one base register. This occurs  when  we
have a lot of code or large arrays.

We  will  also start learning how assembly code is converted
into  "machine  code."  This is what the  computer  actually
runs.  The hex version of the machine code is found  on  the
second column in your listing. The hex numbers left of  each
line of ASSEMBLER code tells you what is in the machine, and
what is actually executed.

Let's  review  some  basic  concepts  about  how  memory  is
addressed.

When we write an instruction like:

         L     3,0(0,4)
         L     3,BLAH
         A     3,4(0,2)
         A     5,ZOOEY

the  second operand in all of these instructions is a memory
address. In the above cases, 0(0,4), BLAH, 4(0,2) and  ZOOEY
all are ways of talking about memory.

The  form  "offset(X1,B1)" is known as an explicit  address.
Offset  is  an integer from 0 to 4095. X1 and  B1  are  both
registers--other than zero.  The address used is  calculated
as follows:

The  contents  of register X1 and register B1 are  added  to
produce  a  temporary value.  Then the integer,  offset,  is
added  to this. This value is used as the address in memory.
(If  either X1 or B1 specify the register zero, they are not
used in the address calculation.)

The  addresses such as BLAH and ZOOEY are converted  to  the
same form by the ASSEMBLER. These are thus known as implicit
addresses, as the base register to be used is implicit.  The
ASSEMBLER determines the base register to be used  from  the
USING instruction(s).

(X1,   the  index  register,  is  never  used  for  implicit
addresses.)

Thus,  if there is only one base register, that will  always
be  used.  That is, if there is only one USING  instruction,
then all implicit addresses will use the value of the second
operand as B1.

For  example, let's say there is one USING.  And, let's call
its  first  operand, address1. E.G., we have something  like
"USING address1,B1".  Let's call the address of the implicit
item,  the address of the second operand of the instruction,
address2.  (E.G., we have something like L 3,address2.)  The
offset  is  the value address2-address1. (If this  value  is
between   0   and   4095,  then  the   second   operand   is
"addressable.")

If  there  is  more than one USING, then the ASSEMBLER  will
check the address associated with each register specified in
a  USING.  Again, call the address of the first  operand  in
that  register, address1. Offset is again address2-address1.
If  the  value  is  between 0 and 4095,  we  will  use  that
register as the base register for this instruction. If  not,
we  keep on trying other registers for one where the  offset
is between 0 and 4095. If none can be found, then the second
operand  is not "addressable" and an error message  will  be
generated.

Thus, we see that when we have large arrays, or even a large
amount  of  code,  we  will have to use multiple  registers.
Otherwise, some of the addresses we will be using  will  not
be  addressable.   That is, there will be no  base  register
that is within 4095 of them.

What  happens  at  run time? How is the  base  register  and
offset that the ASSEMBLER computes as described above, used?
This  is  where the other instruction, we kept  putting  in,
becomes useful. That is the BALR 12,0.

BALR Rega,0 loads register Rega with the address of the next
instruction.  (This is similar to the BALR Rega,Regb or  BAL
Rega,label.  Both of these instructions will also load  Rega
with  the  address of the next instruction.  However,  these
two  branch.  When we write "BALR Rega,0," the machine  just
executes  the next instruction.  The zero is a special  code
meaning  don't branch, I just need the address of  the  next
instruction.)

For example, when we write

         BALR  RegA,0
         USING blah,RegA
blah     equ   *
         L     4,7
         A     3,addr1

The  BALR will make "RegA" contain the address of "blah" the
next  instruction.  The assembler will use RegA as the  base
register  in the "A 3,addr1" instruction. The offset  chosen
will  be addr1-blah. RegA will contain the address of  blah.
When, the "A 3,addr1" is executed, the machine will add  the
"addr1-blah" in the offset field to the "blah" contained  in
RegA.  This,  of course, means that the "effective  address"
will  be  "addr1-blah"  + "blah."  The  "-blah"  and  "blah"
cancel  leaving  addr1.  Recall, an address is  computed  by
adding the offset plus the  base  register. (Remember,  that
the  index register is always zero in implicit addresses and
that means no index-register contents are added.)

Now, that we understand what happens in the simple case of a
single  base register, let's learn how to use multiple  base
registers. Remember, that we have to do this when  our  data
or  code  is  so  big that there are more  than  4096  bytes
between  the address in the USING and the last item we  need
to address. For each base register used, we have to have

a)a  USING  statement  that will tell  the  ASSEMBLER  which
  base  register  to  use  (the second  argument)  and  what
  address   (the   first   argument)  to   subtract   before
  determining the "offset" in the instructions.

b)The  statement that will load the base register  with  the
  address  specified  in  the first argument  of  the  USING
  instruction.  That  way,  when  we  add  the   "addr-first
  argument  of  USING"  to the base  register,  we  get  the
  "addr" used in the instruction.

The first base register, we define with the
         BALR  RegA,0
         USING blah,RegA

For  each of the remaining base registers to be defined,  we
need to determine two addresses in the program:

The  first,  loc1,  will  be some address  that  is  already
addressable. (I.E., it will be a location that will be  less
than  4096  bytes  from some other address."   Ensure,  that
there  is  a  label at this location. Call  it  "lab1")  The
second, loc2, will be some address after loc1, that  is  not
addressable.

At the location, "loc2," we put the ASSEMBLER statement,
length1  equ   *-lab1

Often, loc1 will be the beginning of a large array and  loc2
will  be just after the large array. In this case, "length1"
will simply be the length of the array.

To  establish  addressability for "loc2" and the  next  4095
bytes, we write:

         LA    Regb,loc1
         A     Regb,=a(length1)
         USING loc2,Regb

There  is  one  little gotcha.  Any literals,  such  as  the
=a(length1),  would normally come out  at  the  end  of  the
program. Since these wouldn't be within the address of range
of  all  but  the last base register, we won't  be  able  to
address  them.  The solution to this is to use  the  "LTORG"
command  within 4095 bytes of the beginning of the  program.
This  forces the literals to come out where the initial base
register can apply to them.

Let  us  look at our example. There are three large  arrays,
A,B  and C. Each is larger than 4095 bytes.  Remember, 1024F
means  1024 fullwords at 4 bytes each for a length of  4096.
Thus,  anything that appears after the definition of A won't
be  addressable by the initial base register. Anything  that
appears  after  the definition of B won't be addressable  by
whatever  base  register we set up to handle that  which  is
after  A. (Since, nothing appears after C, we don't have  to
worry about it.) Lines 5 through 7 set up the addressability
of  B  and  B1. Thus, in the above template, "loc1"  is  the
address  of  A,  and  "loc2" is the addres  of  B,  or  more
appropriately, just after A.  "A" will serve  as  "lab1"  in
the template above. "ALENGTH" will serve as the length. Note
that we let "RegA" be register 13, and we add the length  of
A to it.  Then B is associated with base register 13.

We  do  some  assignments statements with B and A,  just  to
prove we can, in lines 9 through 11.

Next, we have to establish addressability for C, C1 and  C2.
"loc1"  is  the address of B1. "loc2" is the address  of  C,
just  after  B1. "BLENGTH" will serve as the  length.   Note
that  we let RegA be 11 and we add the length of B1  to  it.
Then,  C  is  associated with base register 11. After  this,
line  17 and 18 does some assignment statements with C1  and
C2, just to prove we can.