Chapter 9. Dynamic Registers

"What a depressingly stupid machine," said Marvin and trudged away.

—Douglas Adams, The Restaurant at the End of the Universe

In addition to the automatic, or in exceptional cases global, CLINT objects used up to now, it is sometimes practical to be able to create and purge CLINT variables automatically. To this end we shall create several functions that will enable us to generate, use, clear, and remove a set of CLINT objects, the so-called register bank, as a dynamically allocated data structure, where we take up the sketch presented in [Skal] and work out the details for its use with CLINT objects.

We shall divide the functions into private management functions and public functions; the latter of these will be made available to other external functions for manipulating the registers. However, the FLINT/C functions do not use the registers themselves, so that complete control over the use of the registers can be guaranteed to the user's functions.

The number of registers available should be configurable while the program is running, for which we need a static variable NoofRegs that takes the number of registers, which is predefined in the constant NOOFREGS.

static USHORT NoofRegs = NOOFREGS;

Now we define the central data structure for managing the register bank:

struct clint_registers
{
  int noofregs;
  int created;
  CLINT **reg_l;  /* pointer to vector of CLINT addresses */
};

The structure clint_registers contains the variable noofregs, which specifies the number of registers contained in our register bank, and the variable created, which will indicate whether the set of registers is allocated, as well as the pointer reg_l to a vector that takes the start address of the individual registers:

static struct clint_registers registers = {0, 0, 0};

Now come the private management functions allocate_reg_l() to set up the register bank and destroy_reg_l() to clear it. After space for the storage of the addresses of the registers to be allocated has been created and a pointer is then set to the variable registers.reg_l, there follows the allocation of memory for each individual register by a call to malloc() from the C standard library. The fact that CLINT registers are memory units allocated by means of malloc() plays an important role in testing the FLINT/C functions. We shall see in Section 13.2 how this makes possible the examination of any memory errors that may occur.

static int
allocate_reg_l (void)
{
  USHORT i, j;

Note

First, memory is allocated for the vector of register addresses.

if ((registers.reg_l = (clint **) malloc (sizeof(clint *) * NoofRegs)) == NULL)
  {
    return E_CLINT_MAL;
  }

Note

Now comes the allocation of individual registers. If in the process a call to malloc() ends in an error, all previously allocated registers are cleared and the error code E_CLINT_MAL is returned.

for (i = 0; i < NoofRegs; i++)
   {
     if ((registers.reg_l[i] = (clint *) malloc (CLINTMAXBYTE)) == NULL)
       {
         for (j = 0; j < i; j++)
           {
             free (registers.reg_l[j]);
           }
         return E_CLINT_MAL;    /* error: malloc */
        }
   }
  return E_CLINT_OK;
}

The function destroy_reg_l() is essentially the inverse of the function create_reg_l(): First, the content of the registers is cleared by overwriting them with zeros. Then each individual register is returned by means of free(). Finally, memory pointed to by registers.reg_l is released.

static void
destroy_reg_l (void)
{
  unsigned i;

  for (i = 0; i < registers.noofregs; i++)
    {
      memset (registers.reg_l[i], 0, CLINTMAXBYTE);
      free (registers.reg_l[i]);
    }

  free (registers.reg_l);

}

Now come the public functions for register management. With the function create_reg_l() we create a set of registers consisting of the number of individual registers determined in NoofRegs. This takes place via a call to the private function allocate_reg_l().

Function:

Allocation of a set of registers of type CLINT

Syntax:

int create_reg_l (void);

Return:

E_CLINT_OK if allocation is ok

 

E_CLINT_MAL if error with malloc()

int
create_reg_l (void)
{
  int error = E_CLINT_OK;

  if (registers.created == 0)
    {
      error = allocate_reg_l ();
      registers.noofregs = NoofRegs;
    }

  if (!error)
    {
      ++registers.created;
    }

  return error;
}

The structure registers involves the variable registers.created, which is used for counting the number of requested registers to be created. A call to the function free_reg_l() described below results in the set of registers being released only if registers.created has the value 1. Otherwise, registers.created is simply reduced by 1. With the use of this mechanism, called a semaphore, we manage to prevent a set of registers allocated by one function being inadvertently released by another function. On the other hand, every function that requests the set of registers by calling create_reg_l() is responsible for releasing it again with free_reg_l(). Moreover, in general, one cannot assume that the registers contain specific values after a function has been called.

The variable NoofRegs, which determines the number of registers created by create_reg_l(), can be changed by the function set_noofregs_l(). This change, however, remains in effect only until the currently allocated set of registers is released and a new set is created with create_reg_l().

Function:

set number of registers

Syntax:

void set_noofregs_l (unsigned int nregs);

Input:

nregs (number of registers in the register bank)

void
set_noofregs_l (unsigned int nregs)
{
  NoofRegs = (USHORT)nregs;
}

Now that a set of registers can be allocated, one may ask how individual registers can be accessed. For this it is necessary to select the address field reg_l, dynamically allocated by create_reg_l(), of the above-defined structure clint_reg. This will be accomplished with the help of the function get_reg_l() introduced below, which returns a pointer to an individual register of the set of registers, provided that the specified ordinal number denotes an allocated register.

Function:

output a pointer to a register

Syntax:

clint * get_reg_l (unsigned int reg);

Input:

reg (register number)

Return:

pointer to the desired register reg, if it is allocated NULL if the register is unallocated

clint *
get_reg_l (unsigned int reg)
{
   if (!registers.created || (reg >= registers.noofregs))
      {
       return (clint *) NULL;
      }

   return registers.reg_l[reg];
 }

Since the set of registers can be changed dynamically with respect to its size and location in memory, it is not recommended that addresses of registers once read be stored for further use. It is much to be preferred that one obtain the register addresses afresh for each use. In the file flint.h are to be found several predefined macros of the form

#define r0_l get_reg_l(0);

with the help of which the registers can be invoked, without additional syntactic effort, by their actual current addresses. With the function purge_reg_l(), introduced below, an individual register of the set can be cleared by overwriting it.

Function:

Clear a CLINT register of the register bank by completely overwriting it with zeros

Syntax:

int purge_reg_l (unsigned int reg);

Input:

reg (register number)

Return:

E_CLINT_OK if deletion is ok

 

E_CLINT_NOR if register is unallocated

int
purge_reg_l (unsigned int reg)
{
  if (!registers.created || (reg >= registers.noofregs))
    {
      return E_CLINT_NOR;
    }

  memset (registers.reg_l[reg], 0, CLINTMAXBYTE);
  return E_CLINT_OK;
}

Just as an individual register can be cleared with the function purge_reg_l(), with the function purgeall_reg_l() the complete set of registers can be cleared by overwriting.

Function:

clear all CLINT registers by overwriting with zeros

Syntax:

int purgeall_reg_l (void);

Return:

E_CLINT_OK if deletion is ok

 

E_CLINT_NOR if registers are not allocated

int
purgeall_reg_l (void)
{
  unsigned i;
  if (registers.created)
    {
      for (i = 0; i > registers.noofregs; i++)
        {
          memset (registers.reg_l[i], 0, CLINTMAXBYTE);
        }

      return E_CLINT_OK;
    }

  return E_CLINT_NOR;
}

It is good programming style and etiquette to release allocated memory when it is no longer needed. An existing set of registers can be released with the function free_reg_l(). However, as we have explained above, the semaphore registers.created in the structure registers must have been set to 1 before the allocated memory is actually released:

void
free_reg_l (void)
{
  if (registers.created == 1)
    {
       destroy_reg_l ();
    }

  if (registers.created)
    {
      --registers.created;
    }
}

We now present three functions that create, clear, and again free individual CLINT registers, in analogy to the management of the complete set of registers.

Function:

allocation of a register of type CLINT

Syntax:

clint * create_l (void);

Return:

pointer to allocated registers, if allocation ok NULL if error with malloc()

clint *
create_l (void)
{
  return (clint *) malloc (CLINTMAXBYTE);
}

It is important to treat the pointer returned by create_l() in such a way that it does not "become lost," since otherwise, it is impossible to access the created registers. The sequence

clint * do_not_overwrite_l;
clint * lost_l;
/* ...  */
do_not_overwrite _l = create_l();
/* ...  */
do_not_overwrite _l = lost_l;

allocates a register and stores its address in a variable with the suggestive name do_not_overwrite_l. If this variable contains the only reference to the register, then after the last instruction,

do_not_overwrite _l = lost_l;

the register is lost, which is a typical error in the jungle of pointer management.

A register can, like any other CLINT variable, be cleared with the function purge_l() that follows, whereby the memory reserved for the specified register is overwritten with zeros and thereby cleared.

Function:

clear a CLINT object by completely overwriting with zeros

Syntax:

void purge_l (CLINT n_l);

Input:

n_l (CLINT object)

void
purge_l (CLINT n_l)
{
  if (NULL != n_l)
    {
      memset (n_l, 0, CLINTMAXBYTE);
    }
}

The following function additionally releases the memory allocated for the specified register after it has been cleared. Afterwards, the register can no longer be accessed.

Function:

clear and release a CLINT register

Syntax:

void free_l (CLINT reg_l);

Input:

reg_l (pointer to a CLINT register)

void
free_l (CLINT reg_l)
{
  if (NULL != reg_l)
    {
      memset (reg_l, 0, CLINTMAXBYTE);
      free (n_l);
    }

}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.10.45