ZEBRA: A Beginner's Guide

This beginner's guide is organised as follows. All users of SNOMAN should read Part I. You need only study Part II if you are going to write code to create or modify data structures.



Part I : Basic Topics



ZEBRA: A Memory Manager

FORTRAN 77 is a static language, that is to say, all variables and arrays have to be declared and defined at compile time. For example, suppose a standard FORTRAN 77 program is to be written to describe the transport of a set of particles through some detector. The code has to deal with tracks and vertices. The code writer may decide that each vertex needs 15 words of data and that a maximum of 30 vertices are needed, and makes the following declaration:- real vertex(15,30) In a similar way there might be:- real track(10,50) The problem is that the writer has to decide at compile time the absolute limit on the number of tracks and vertices. As the project develops, more arrays have to be defined and each will be subject to an arbitrary maximum.

A second limitation is that there is no pointer variable in FORTRAN 77, so there is no standard way to link one array to another. Of course an array index is often used in such cases. It is this principle that memory managers such as ZEBRA are based on. ZEBRA declares a single huge array, with a declaration similar to the following:-

integer iq(1000000) real rq(1000000) equivalence ( iq , rq ) common /z/ iq When the application code requires some storage, a request is passed to ZEBRA which reserves space for it in the IQ/RQ array and returns a pointer to the reserved part. In memory manager terminology, the reserved area is called a BANK and the pointer is called a LINK . The bank may to used to store both integers and reals and hence the need to equivalence the integer and real arrays. Every bank has a 4 character BANK NAME e.g. 'MCVX' and a BANK NUMBER . The name and number have no significance to ZEBRA but are stored as part of the bank.

A simplified scheme of the way ZEBRA works is shown below:-

Application Code ZEBRA ------------------------------------------------------------------------------ | | (1) common/z/ iq(1000000) | common/ZEBRA_sys/ lmax,lcur (2) real rq(1000000) | equivalence (iq,rq) | | | | (3) call MZINIT(1000000) ---+--> subroutine MZINIT(i_max) (4) | lmax = i_max | lcur = 0 <--+-- return | | | (5) call MZLIFT(l,20) ---+--> subroutine MZLIFT(link,i_size) (6) | link = lcur | lcur = lcur + i_size | if (lcur .gt. lmax) then | generate store full trap (7) | endif <--+-- return | | | (8) iq(l+1) = 123 | rq(l+2) = 456. | ... | rq(l+20) = -456. | | | | | (9) call MZDROP(l) ---+--> subroutine MZDROP(l) | mark bank l as dead. <--+-- return | The main feature are:-
  1. The user declares a single fixed size array, called the DYNAMIC STORE.
  2. The ZEBRA system declares its own system variables.
  3. The user calls to MZINIT initialises ZEBRA.
  4. ZEBRA records the store size and resets a pointer to the next free location.
  5. As required, the application requests a bank from ZEBRA. In this case the bank is to contain 20 data words.
  6. ZEBRA checks to see if the store is now full. If not, the caller is returned a link to the bank, and the free pointer is updated.
  7. If the store is full, it generates a store full trap back to a high level routine, it does NOT return control to the caller in this case.
  8. The application code is now free to store data in the bank.
  9. When the bank is no longer required the link is passed to MZDROP and the bank is marked as dead by ZEBRA. The space occupied by the dead bank is not reclaimed until Garbage Collection
So ZEBRA solves both of the FORTRAN deficiencies; it has memory allocation at execution time, and the bank links serve as pointers. Of course, there is nothing magic here, this is still all done in a single fixed length array, but even languages with dynamic memory allocation have to run within fixed sized memory machines! Changing the size of the dynamic store is very simple so long as the true size is declared in only one routine, for SNO the routine is DEFINE_MEMORY_SIZE), and kept dummy in all others.

Bank Data Words

The data words of a bank are stored in the memory immediately after the link, as shown in the diagram below for a bank with ND data words. IQ(L)---------->| | +----------------------+ IQ(L+1) | Data word 1 | ... | ... | IQ(L+ND) | Data word ND | +----------------------+ So, this leads to code like this:- energy = rq(l+3) iq(l+8) = medium code which is unreadable by anyone, and this includes the author after a few months have elapsed! The array name iq (integer) or rq (real) simply means "dynamic store", the meaning of the variables has been devolved down to the link and the offset. In terms of structures, the link is the structure variable and the offset is the field name.

The first step to improve readability it to follow a standard convention of most ZEBRA codes in the naming of links. All links should be of the form:-

Lbbbb, where bbbb is the bank name
So, for bank MCVX, the link name would be LMCVX. If more than one MCVX link is required, further names are formed by adding a postfix e.g. an end vertex might be called LMCVXE or LMCVX_END.

The second step is to use the SNO convention for offsets, using parameters of the form:-

Kbbbb_fff, where fff is the offset name
For example, KMCVX_PSX is the X position of the MCVX bank. A complete set of offset mnemonics for each bank can be found in the file:-
...snoman/n_nn/code/bank_bbbb.inc, e.g. bank_mcvx.inc
and is also shown in the corresponding Event Data Structure diagram. For example, see MCVX.

Using both these conventions, access to a bank data word now looks like this:-

rq(lmcvxc + KMCVX_PSX) and, when reading such an access, you should interpret is as:-
"The PSX field of an MCVX is the dynamic store Q"

Warning: Unlike languages with true structures, there is nothing to prevent you from misforming either the link or the offset and consequently accessing the wrong word of the dynamic store. Reading from the wrong address can give unpredictable errors, and writing to it will damage the data structure. Using the offset parameters can eliminate one source of error, but you should take great care that your links are correct, particularly if you are generating memory requests. This is discussed further in Event Relocation and Local Links.


Bank Status Word

The link to a bank points to the word before the first data word. This word, which is also owned by the bank, is called the bank's STATUS word:- IQ(L-1) | | +----------------------+ IQ(L)---------->| Status | +----------------------+ IQ(L+1) | Data word 1 | It consists of a set of one-bit flags. The bottom half of the words (bits 0 to 17) are available for the application. As with the data word offsets, a set of bit address parameters are defined that will work with the bit manipulation routines such as BTEST and IBSET. So, for example, to test the SRC bit of an MCVX, you would code:- if ( btest( iq(lmcvx) , KMCVX_SRC ) ) then The top half, (bits 18 to 31) are reserved for ZEBRA. For example a bit in a bank's status word is set to mark it as dead.

Warning:The ZEBRA documentation uses the numbering convention which is shifted up by one on the standard convention. So the user bits are 1 to 18 and the system bits are 19 to 32.


Bank Links

Dynamic allocation of single banks is of limited use, the real power of ZEBRA comes when a bank stores a link to another bank. Warning You MUST NOT store links as data words for banks can move and the system needs to be able to find and update links. This is discussed in:- Event Relocation and Local Links Instead a separate area of a bank is reserved for links. When asking ZEBRA to lift a bank, the user not only asks for ND words of data, but also NL links. The links are stored in an area before the status word:- LQ(L-NL-1) | | +----------------------+ LQ(L-NL) | Link NL | ... | ... | LQ(L-1) | Link 1 | +----------------------+ LQ(L)---------->| Next-link | +----------------------+ LQ(L+1) | Up-link | +----------------------+ LQ(L+2) | Origin-link | +----------------------+ IQ(L-5) | | +----------------------+ IQ(L-4) | | +----------------------+ IQ(L-3) | | +----------------------+ IQ(L-2) | | +----------------------+ IQ(L-1) | | +----------------------+ IQ(L)---------->| Status | +----------------------+ IQ(L+1) | | The 7 words between the link area and the status word are described in Horizontal Links, The Origin Link and Bank Information

There are a number of points to notice about this diagram:-

Just as with the data words, there are a series of parameters for link word offsets. This time the naming convention is:-
Kbbbb_cccc, link from bbbb to cccc
for example:- lmctk = lq(lmcvx - KMCVX_MCTK) Study this carefully; notice the LQ rather than IQ or RQ, and the negative offset!

The value 0 is used to denote a null link.

There are 2 types of links:-

A bank can neither, either, or both, but if both, the structural links are always below the reference links. If a bank has NL links, of which NS are structural, then the layout is:- | | +----------------------+ LQ(L-NL) | Link NL | ... | ... | Reference Links LQ(L-NS-1) | Link NS+1 | +----------------------+ LQ(L-NS) | Link NS | ... | ... | Structural Links LQ(L-1) | Link 1 | +----------------------+ LQ(L)---------->| | + +

Bank Structural Links

Banks are connected into a data structure using STRUCTURAL links; they define the data structure. All banks are connected into a data structure by a single structural link held in another bank; consequently it is only possible to build simple tree data structures. However Reference Links can be used for more complex structures. Of course, the topmost bank in the tree cannot be connected to another; ZEBRA has a special structural link for that.

There are 2 sorts of structural links:-


Vertical (Down) Links

Except for the topmost bank, every bank is supported by a vertical structural link from the bank above. For example, in the Event Data Structure diagram, the MCTK is shown as being connected to the MCVX:- MCVX | +------MCTK MCVX is the Monte Carlo VerteX bank and MCTK is the MC TracK bank that comes from it.
A vertical structural link implies dependence
So, in this case, the MCTK depends on its MCVX which is said to support it. If the MCVX is dropped then the MCTK it supports must also be dropped and ZEBRA does this automatically. In terms of the representation: if a vertex is dropped then the tracks that are produced by it must also be dropped.

To step down from a MCVX to its MCTK:-

lmctk = lq(lmcvx - KMCVX_MCTK) Every bank has an Up-link held at LQ(L+1) that allows the supporting bank to be accessed:- | | +----------------------+ LQ(L)---------->| Next-link | +----------------------+ LQ(L+1) | Up-link | +----------------------+ | | E.g.:- lmcvx = lq(lmctk+1) There is no parameter for the +1 offset as it is the same for every bank.

Horizontal Links

If a vertex has a single outgoing track then a single vertical structural link is sufficient to define the relationship between them. But what if the vertex has several tracks? Clearly we need several links, but where will they be stored? It would NOT be a good idea to store them in the vertex bank for then, each time a new track was added, the vertex would also have to be extended to hold the new link. The solution is in the concept of a LINEAR CHAIN of banks.
Every bank has a HORIZONTAL link to the "Next" bank in the chain
This link is stored in LQ(L):- | | +----------------------+ LQ(L)---------->| Next-link | +----------------------+ | | So, once the first MCTK has been reached, further MCTKs can be accessed using:- lmctk = lq(lmctk)
A horizontal structural link implies independence; normally there is no significance to the order in which banks are placed in the chain
In our representation, every outgoing track depends on the vertex from which it comes, but each track is logically independent of all others. When ZEBRA drops a bank the user is offered a choice, to just drop the one bank or to follow the horizontal link and drop the rest of the linear chain.

Typical code to process all the tracks at a vertex looks like this:-

lmctk = lq(lmcvx - KMCVX_MCTK) do while ( lmctk .ne. 0 ) ... process current MCTK ... lmctk = lq(lmctk) enddo This simple code fragment works regardless of the number of outgoing tracks, including none at all. From each MCTK the MCVX could again be reached by using lq(lmctk+1) i.e. the Up-link.

ZEBRA provides a number of utilities to process linear chains of banks. For example NZBANK counts the number of banks in a chain, given a link to the start of the chain. LFZFIND searches a chain for a bank having a specified value in a specified word. A summary of the most commonly used utilities can be found in ZEBRA Routines


The Origin Link

The Origin link is the third of the three links that all banks have:- | | +----------------------+ LQ(L)---------->| Next-link | +----------------------+ LQ(L+1) | Up-link | +----------------------+ LQ(L+2) | Origin-link | +----------------------+ | | It contains the address of the word that holds the structural link to the bank. Consider:- MCVX | +------MCTK1------MCTK2 The Origin link for MCTK1 is LMCVX - KMCVX_MCTK while the Origin link for MCTK2 is LMCTK1. With a little care, this link can be used to traverse a linear chain in the reverse direction:- lmctk_next = lq(lmctk+2) if (lq(lmctk_next) .ne. lmctk) lmctk_next = 0 lmctk = lmctk_next However, there is little need ever to do this; after all there is normally no significance to the ordering of banks in a chain. The Origin-link is used primarily by ZEBRA itself, when deleting a bank in a chain, so that it can bridge across the removed bank.

Bank Reference Links

REFERENCE links appear above any structural links in the bank's link area:- | | +----------------------+ LQ(L-NL) | Link NL | ... | ... | Reference Links LQ(L-NS-1) | Link NS+1 | +----------------------+ LQ(L-NS) | Link NS | ... | ... | Structural Links LQ(L-1) | Link 1 | +----------------------+ LQ(L)---------->| | +----------------------+ The bank's Structural Links can only be used to build simple trees, but the reference links can be used to express any arbitrary structure, e.g. a circular ring of buffers or a push-down stack.
Reference links imply no logical dependence; links may be made or broken at any time without changing the underlying data structure.
A bank can have any number of reference links and can be pointed to by any number of reference links. The reference links need not even point to a bank's Status Word. It could, for example, point to a particular data word in the bank that has special significance for the bank holding the link. At the time of writing this guide, all of our reference links point to bank status words. Any departure from this practice will be clearly documented.

An example of a reference link is the link between an MCTK and the MCVX it ends on. The link offset for this is KMCTK_MCVX. The following code can be used to step from one vertex to the next, following its first outgoing track:-

lmctk = lq(lmcvx - KMCVX_MCTK) lmcvx = lq(lmctk - KMCTK_MCVX)

Warning:Reference links to a bank may continue to point to it after it has been dropped.

When a bank is dropped, it is not immediately removed from the data structure but has a system status bit set to mark it as dead. As part of the dropping process, its only supporting structural link is either set to zero or bridged across to the next bank in the chain. However, trying to reset reference links to it is very time consuming; there may be any number and they may be located in any bank. This should not be a problem; the application code should know what reference links exist and should clear them when it drops the bank. In any case, if Garbage Collection takes place then all links have to be updated and any pointing into a dead bank will be set to zero.


Bank Information

Between a bank's Links and its Status Word there is a set of 5 words holding information about the bank:- LQ(L+2) | Origin-link | +----------------------+ IQ(L-5) | IDN | Numeric bank identifier +----------------------+ IQ(L-4) | IDH | Hollerith bank identifier +----------------------+ IQ(L-3) | NL | Total number of links +----------------------+ IQ(L-2) | NS | Number of structural links +----------------------+ IQ(L-1) | ND | Number of data words +----------------------+ IQ(L)---------->| Status | These can be used to check on the bank's identifiers and size. For example it is often useful to check in IQ(L-1) to see how many data words a bank has. The bank's Hollerith identifier is 4 characters long, the function CHAINT can be used to convert it to a 4 character variable, e.g.:- print *, 'The name of this bank is' , chaint( iq(l-4) )

Stores and Divisions

Stores

For SNOMAN, the Q dynamic store (array IQ/RQ/LQ) is the ZEBRA memory that is of most interest as it is this that holds the Event Data Structure However, ZEBRA is not restricted to a single memory; it can manage up to 16 simultaneously. Within SNOMAN there are two: the Q store and the CONS store. As with Q, this consists of the 3 arrays ICONS/RCONS/LCONS. The CONS store is used to hold Titles Banks.

If HBOOK is being used then ZEBRA is managing a third store defined by the /PAWC/ common block. However, beyond defining the size of this store, using the routine DEFINE_MEMORY_SIZE, no reference to this is store is made by SNOMAN.

Associated with each store is a STORE INDEX used to identify the store to ZEBRA. For the first store defined (the Q store), this index is zero, for all other stores the number is non-zero. For the CONS store the store index is MTSTOR, defined in mt.inc.

Divisions

ZEBRA divides an individual store up into DIVISIONS. When a store is first initialised by ZEBRA, 3 divisions are defined:- +-----------------------------------------+------------------------+ | | | | Division 1 --> <-- Division 2 | <-- Division System | | | | +-----------------------------------------+------------------------+ and further divisions up to a limit of 20 can be added. Each division operates like a separate dynamic store, the arrows in the above diagram show the direction of growth as new banks are added.

Divsions allow global operations on entire data structures. Consider, for example, the erasure of a data structure. This could be done, bank by bank, using MZDROP. But such an operation would be very time consuming. There is a much faster way of doing this using MZWIPE, which clears one of more divisions essentially by resetting the pointer to the first free word in the division. Traditionally, division 1 is used as a short term store and division 2 for more permanent data. So an analysis program could use division 1 to sequentially read in event data structures, wiping the division between events, and use division 2 to accumulate data derived from the events. SNOMAN uses division 1 of the Q store for event data structures and division 2 for Run Headers and other permanent data. Division 2 of the CONS store hold titles banks.

Each division is assigned a DIVISION INDEX, based on the store index as follows:-

division index = store index + division number
The division index for the event data structure is IDIV_EVENT which is defined in z.inc. The titles banks division is MTDIV, which is defined in mt.inc.

Part II : Advanced Topics

You need only study Part II if you are going to write code to create of modify data structures.



Store Full and Garbage Collection

Each time ZEBRA receives a request for a new bank it attempts to satisfy it by appending a new bank to the free end of the required Division (not necessarily the high address end). If a request to drop a bank is made, then it is marked as dead by setting a bit in the system part of the bank's Status Word; the space is not recovered immediately. Eventually a time may come when there is insufficient space to store the next new bank. If this happens GARBAGE COLLECTION takes place. This involves deleting all dead banks and moving up all live banks to so as to recover as much space as possible. As part of the process of moving banks, Event Relocation takes place, updating all links as follows:-

In this way as much store a possible is recovered for new banks. If insufficient banks have been dropped, then even this procedure will eventually fail and ZEBRA will not be able to complete a memory request. In this case a STORE FULL trap is generated which results in control being transferred to the high level routine QNEXT (the routine that calls EVMAIN). This means that:-

MZLIFT never returns a failure due to lack of memory.
This makes writing code that lifts banks easier as the store full condition, which could in principle happen at any time, does not have to be catered for. Instead all such errors are handled by the high level code which is the most appropriate place to deal with such problems. If it is required to write code that does not want to loose control because a store full has occurred, the routine MZNEED can be used. It returns the space that is currently available and this can be used to decide whether to call MZLIFT.

Event Relocation and Local Links

Garbage Collection involves moving banks in memory, which in turn involves updating all the links that point to them, a process called EVENT RELOCATION. In order for ZEBRA to update all links it needs to know where they all are. ZEBRA can manage links stored in three places:-

If you look in z.inc, you will see that an array of structural links and an array of reference links are declared:-

COMMON /Z/ Z_FENCE, + Z_STR_LINKS, + Z_REF_LINKS, + Z_WORK with INTEGER Z_STR_LINKS(100) INTEGER Z_REF_LINKS(200) and EQUIVALENCE + (Z_STR_LINKS( 1), LMAST), ... + ... (Z_REF_LINKS(24), LMC), So, if event relocation takes place, LMAST and LMC will be correctly updated.

A potentially very serious bug can occur if a user takes a copy of a link into a local variable and continues to use it after event relocation may have taken place. Consider this apparently innocent, if rather odd piece, of code:-

... include 'z.inc' integer my_link ... lmc = lq( lmast - KMAST_MC ) my_link = lmc call mzlift(...) if ( my_link .ne. lmc ) print 'Event relocation has occurred' This code could be run many times without the print statement being executed. In this case my_link is still valid and can be used to access the MC bank. However, at any time the call to MZLIFT might cause garbage collection to occur and this might mean the MC banks moves. Then the printed message appears and using my_link to access MC would be a disaster!!. Retrieving information from MC would produce junk and writing to MC would damage the data structure. The unpredictability of the error, coupled with the fact that it will only occur rarely, means that it could be very hard to track down. It must be avoided at all costs!

Fortunately, avoiding the problem is not difficult so long as the following steps are carried out:-

  1. Are local links O.K.? If a routine does not, either directly or indirectly, result in new banks being added to the data structure, then local links are fine. If new banks are added the code must use managed links.
  2. Using the Bank Links in z.inc For each event bank there is at least one reference link defined in z.inc. Between calls to processors these links are freely available; only the structural link LMAST is assumed to be defined and must not, of course, be changed. If adding code to a processor then some of these links are already in use; the author must check the processor code.
  3. Using the Temporary Links LTMPn in z.inc There are a set of reference links LTMP1, LTMP2... that can be used by any piece of code. Of course, if the code calls other SNOMAN code, they too are free to use these links, so they should only be used to protect links across calls to the ZEBRA system.
  4. Refreshing Local Links If the managed links listed above are insufficient then they can be augmented with local links so long as these links are refreshed after every memory request. Clearly this is only appropriate if the links are easy to rederive from managed links.
  5. Creating a New Pool of Managed Links This is the ultimate weapon in cases where none of the above solutions is satisfactory. ZEBRA makes it very easy to define a new pool of managed links (which must be stored in a common block). There are two types of pool:- Permanent links are best suited for global sets of links. The drawback of them is that ZEBRA has to update them each time event relocation takes place. If there were many such pools, this could slow the code down. For local links, used only by one piece of code, temporary links are better. The code can turn the management on at entry and off at exit and so fine tune the management to the minimum required.
Warning: Managed links always refer to a specific dynamic store. In the above discussion it is assumed that the user is interested in the Q dynamic store. If links are required for the CONS stored then they have to be declared separately. However, normally access to CONS banks is via the Constants Management link access blocks. The Constants Management system declares such blocks as link pools so the links Lxxxx and LDxxxx are already managed.

A related problem can occur when passing bank data to subroutines and functions. Consider:-

... include 'z.inc' ... lmc = lq( lmast - KMAST_MC ) call print_time( iq(lmc + KMC_JDY) ) This time only the protected link LMC is used; so the calling routine is protected. But what if PRINT_TIME caused a garbage collection? The data it has been passed could now be invalid. The problem is that, by passing part of a bank as an array, we have implicitly created a local link. So long as PRINT_TIME does not disturb the memory, this is quite safe. If this is not the case then the call must be changed to:- lmc = lq( lmast - KMAST_MC ) call print_time( lmc , KMC_JDY ) Although even now we could still be in trouble. When compiling PRINT_TIME, there is nothing to stop an optimising compiler from taking a local copy of the variables passed to it as a local copies can be faster to access. Indeed, compilers habitually did this in the past with scalars arguments, but not with arrays. This was used to defeat the optimiser by locally defining the link as an array:- suboutine print_it( link , offset ) include 'z.inc' integer link(1) , offset ... print * , iq( link(1) + offset ) Although the compiler may have copied OFFSET, it would have just kept the address of LINK and so if the value of LINK(1) changed, the code would still work correctly. Were you brave enough to look at the ZEBRA source, you would see this trick being played1 A more transparent way to deal with the problem is to copy link arguments into a protected area and return the value to the argument variable before returning. To repeat though, this need only be of concern when calling routines that could, directly or indirectly, trigger a garbage collection.

Bank Format

Every bank carries a description of its format. This can be used to determine the type (integer, real, hollerith etc.) of every word of the bank. The description is stored at the start of the bank, before the link area:- +----------------------+ LQ(L-NL-NIO-1) | IOcb | NL+NIO+12 | +----------------------| LQ(L-NL-NIO) | I/O opt. 1 | ... | ... | LQ(L-NL-1) | I/O opt. NIO | +----------------------+ | | The purpose of a bank's format is to allow the bank to converted from one computer hardware to another, and also to permit simple bank printing.

A complete discussion of this subject is beyond the scope of this guide, only an outline will be given here. For more detail see MZFORM in the ZEBRA Reference Manual. To define a bank format the user has to pass a format string the MZFORM. The syntax of this string is given in the table below:-

format_string = head or head / tail or / tail head = sector sector ... tail = sector sector ... sector = count type count = number e.g .24 (fixed length) or - (rest of bank) or * (dynamic - word count stored in bank) type = B (bit) or I (integer) or F (floating point) or D (double precision) or H (4 character hollerith) or S (self describing) If the length of a bank exceeds the description, the tail is used again i.e. the extended format consists of the head (if any) followed by a tail (if any) repeated as often as required. Some simple examples:- -F whole bank is floating point 3I -F 3 integers, the rest floating 3B *I -F 3 words of bits, a dynamic sector of integers, rest floating *I / 2I *F a dynamic sector of integers then a tail of 2 integers and a dynamic sector of floats

Warning: Word counts give the number of 4 byte words notthe number of objects. This means that 24D means 24 words i.e. 12 double precision words.

The format string is passed to MZFORM which converts it into an I/O characteristic, which will eventually be stored in the bank. However, MZFORM stores this characteristic in a system table, and only returns a single word index to it. The user also passes the bank name to MZFORM e.g :-

call MZFORM( 'MCVX' , '2I 3F 2D 4I 3F 1I' , ixio ) Subsequently, when lifting an MCTK bank, the user can either ask ZEBRA to find the characteristic for the 'MCVX' bank or can pass IXIO.

Lifting a Bank

To lift a bank, the user first has to construct a 5 word vector giving the bank's:-
  1. name (IDH)
  2. total number of reference and structural links (NL)
  3. number of structural links (NS)
  4. number of data words (ND)
  5. Bank Format I/O characteristic (IXIO).
For example:- integer mmmcvx(5), nd, nl, ns, iixo, intcha data nl / 2 /, ns / 1 /, nd / 15 / call MZFORM( 'MCVX' , '2I 3F 2D 4I 3F 1I' , ixio ) mmmcvx(1) = intcha( 'MCVX' ) mmmcvx(2) = nl mmmcvx(3) = ns mmmcvx(4) = nd mmmcvx(5) = ixio For all the standard event data banks such vectors already exist in:-
bnkdef.inc

The next step is to call MZLIFT to create the bank and connect it into the data structure e.g.:-

call MZLIFT( idiv_event , lmcvx , lmc , -KMC_MCVX , mmmcvx , 0 ) This lifts a bank in the IDIV_EVENT division of the Q store. The link to the bank is returned in LMCVX, and the bank is connected below the MC banks using the -KMC_MCVX Vertical Link. The final 0 means that all data words are to be preset to zero.

It is possible to alter the size of an existing bank, either increasing or decreasing the number of links or data words, using MZPUSH. For example, to add 2 links and remove 3 data words from an MCVX bank:-

call MZPUSH( idiv_event , lmcvx , +2 , -3 , '.' ) For other ways to call MZLIFT and more details about MZPUSH, see the ZEBRA reference manual.


Dropping a Bank or Data Structure

To drop a bank and the subtree that it supports, use MZDROP e.g.:- call MZDROP( 0 , LMCVX , '.' ) The leading 0 is the Store Index i.e. the Q store. The trailing character string is an option string. By setting it to the default i.e. '.' the horizontal link is not followed, instead the selected MCVX is dropped and the linear chain is bridged across it. Other possible options are:-

To wipe a complete divisions :-

call MZWIPE( i_division ) where I_DIVSION is the division index e.g. IDIV_EVENT.

Part III : Quick Reference



Bank Layout and Access

  Access              Contents                  Description and Example Use
 
                +----------------------+
LQ(L-NL-NIO-1)  | IOcb    |  NL+NIO+12 |   I/O control/Offset to bank centre
                +----------------------|
LQ(L-NL-NIO)    |     I/O opt. 1       |
...             |       ...            |   Extra I/O descriptor words (0 to 16)
LQ(L-NL-1)      |    I/O opt. NIO      |
                +----------------------+
LQ(L-NL)        |      Link NL         |
...             |       ...            |   Reference links
LQ(L-NS-1)      |     Link NS+1        |   lmcvx_e = lq(lmctk1+KMCTK_MCVX)
                +----------------------+
LQ(L-NS)        |     Link NS          |
...             |       ...            |   Structural (down) links
LQ(L-1)         |      Link 1          |   lmctk1 = lq(lmcvx+KMCVX_MCTK)
                +----------------------+
                |                      |
LQ(L)---------->|     Next-link        |   Link to next bank in linear chain
                |                      |   lmctk2 = lq(lmctk1)
                +----------------------+
                |                      |
LQ(L+1)         |     Up-link          |   Link to supporting bank
                |                      |   lmcvx = lq(lmctk2+1)
                +----------------------+
                |                      |
LQ(L+2)         |    Origin-link       |   Address of supporting link
                |                      |   lmctk1 = lq(lmctk2+2)
                +----------------------+
                |                      |
IQ(L-5)         |        IDN           |   Numeric bank identifier
                |                      |   ibank_no = iq(lbank-5)
                +----------------------+
                |                      |
IQ(L-4)         |        IDH           |   Hollerith bank identifier (4 chars)
                |                      |   print * , 'Bank name:' , chaint( iq(lbank4) )
                +----------------------+
                |                      |
IQ(L-3)         |        NL            |   Total number of links
                |                      |   num_links = iq(lbank-3)
                +----------------------+
                |                      |
IQ(L-2)         |        NS            |   Number of structural links
                |                      |   num_str_links = iq(lbank-2)
                +----------------------+
                |                      |
IQ(L-1)         |        ND            |   Number of data words
                |                      |   num_data = iq(lbank-1)
                +----------------------+
                |                      |
IQ(L)---------->|      Status          |   Status bits: 0-17 user, 18-31 system
                |                      |   if ( btest( iq(lmcvx) , KMCVX_SRC) ) ) then
                +----------------------+
IQ(L+1)         |    Data word 1       |
...             |       ...            |   Data words
IQ(L+ND)        |       ...            |   energy  = rq(lmctk+KMCTK_ENE)
                |   Data word ND       |   id_part = iq(lmctk+KMCTK_IDP)
                +----------------------+
 
Where:- MCVX ...MCVX_E ... reference link | . --- structural link | . +------MCTK1------MCTK2

ZEBRA Routines

The full ZEBRA Reference manual can be found at http://wwwasdoc.web.cern.ch/wwwasdoc/zebra_html3/zebramain.html This section has been shamelessly copied from that reference!

Data Retrieval

LZFIND,NZBANK et al. - Utilities to interrogate a linear structure
LZHEAD - find the first bank of a linear chain

Data Structure Reorganisation

ZSHUNT - Change structural relation
ZTOPSY et al. - Utilities to operate on a linear structure
ZSORT - Utility to sort the banks of a linear structure

Data Structure Modification

MZLINK/MZLINT - Initialise a link area
MZBOOK/MZLIFT - Bank creation
MZPUSH - Alter the size of a bank
MZDROP - Drop a bank and its dependents
MZWIPE - Reset a complete division to empty
MZGARB - Garbage collection requested by the user
MZFORM et al. - Handle the I/O characteristic

LZFIND,NZBANK et al. - Utilities to interrogate a linear structure

These routines perform service functions for linear structures. The parameter LLS is the address of the first bank of the linear structure.

                      +----------------------------+
                      | !LF =  LZLAST(IXSTOR,!LLS) |
                      +----------------------------+
                                  

searches the linear structure pointed to by LLS for its end. It returns in LF the address of the last bank in the structure. LF = 0 is returned if the structure is empty.

                   +----------------------------------+
                   | !LF =  LZFIND(IXSTOR,!LLS,IT,JW) |
                   +----------------------------------+
                                  

searches the linear structure pointed to by LLS for the first bank containing IT in word JW; it returns its address in LF. If none: LF=0.

                 +--------------------------------------+
                 | !LF =  LZLONG(IXSTOR,!LLS,NW,ITV,JW) |
                 +--------------------------------------+
                                  

has the same function as LZFIND, but ITV is a vector of NW words expected in words JW to JW+N-1 of the bank.

                +----------------------------------------+
                |!LF =  LZBYT(IXSTOR,!LLS,IT,JBIT,NBITS) |
                +----------------------------------------+
                                  

has the same function as LZFIND, but it looks for a bank having IT in byte (JBIT,NBITS) of the status word.

                 +--------------------------------------+
                 |!LF =  LZFVAL(IXSTOR,!LLS,VAL,TOL,JW) |
                 +--------------------------------------+
                                  

has the same function as but it looks for a bank having in word JW a floating point number which is equal to VAL within the tolerance TOL.

                       +--------------------------+
                       | N =  NZBANK(IXSTOR,!LLS) |
                       +--------------------------+
                                  

counts the number of banks in the linear structure pointed to by LLS.

                    +--------------------------------+
                    | N =  NZFIND(IXSTOR,!LLS,IT,JW) |
                    +--------------------------------+
                                  

searches like LZFIND, but for all banks. It returns the number of such banks in N and stores the addresses of the first 100 such banks into IQUEST, starting at IQUEST(1).

                  +------------------------------------+
                  | N = NZLONG (IXSTOR,!LLS,NW,ITV,JW) |
                  +------------------------------------+
                                  

searches like LZLONG, but for all banks. It returns the number of such banks in N and stores the addresses of the first 100 such banks into IQUEST, starting at IQUEST(1).


LZHEAD - find the first bank of a linear chain

This routine will try to find the first bank of the linear structure of which the bank at LGO is a member. It does this by following the path indicated by the "origin" link of the bank at LGO, and using its "up" link.

                      +----------------------------+
                      | !LF =  LZHEAD(IXSTOR,!LGO) |
                      +----------------------------+
                                  

It returns the address of the first bank of the linear structure as the function value; or zero if there is trouble.

If the linear structure is not a top-level structure, ie. if the up-link LUP is non-zero, the path of origin-links should end in the link region of the bank at LUP, at a word whose off-set JBIAS can then be calculated. This is returned:

      IQUEST(1) negative:  = JBIAS

ie. LQ(LUP+JBIAS) contains the address of the first bank of the linear structure.

If LUP is zero, the origin-path should end at a word outside the bank space of the store IXSTOR, which word should contain the address of the first bank of the linear structure. In this case LZHEAD returns:

      IQUEST(1) = 1: top-level structure
      IQUEST(2) = LS, relative adr of the supporting link-area link,
                      ie. LQ(LS) contains LF
If LUP is zero, and if the origin-link in the last bank in the path is zero, this is a stand-alone structure, in which case LZHEAD returns:

      IQUEST(1) = 2: stand-alone structure

If there is trouble, LZHEAD will return the function value zero, and set:

      IQUEST(3) = 1   if LGO is zero
                  2   if LUP non-zero and the last origin-link
                      points outside bank-space
                  3   if LUP non-zero and LQ(LUP+JBIAS) does not
                      point to the last bank in the origin-path
                  4   if LUP zero, and LQ(LS) does not point to
                      the last bank in the origin-path.


ZSHUNT - Change structural relation

Unlike in HYDRA, and because of the reverse pointers, the operation of moving a bank by re-linking from one data-structure to another one is a non-trivial operation. The routine ZSHUNT is provided to execute such an operation.

ZSHUNT may be used to extract either a single bank (IFLAG=0) or a whole linear structure (IFLAG=1) from the old context, for insertion into the new context as described by the parameters LSUP and JB, which have the same significance as in MZLIFT.

               +------------------------------------------+
               |CALL  ZSHUNT (IXSTOR,!LSH, !LSUP,JB,IFLAG) |
               +------------------------------------------+
                                  
with

      IXSTOR  index of the store, zero for the primary store;
              IXDIV, the index of the division containing
              the bank to be shunted, may be given instead
        !LSH  address of the bank or of the linear structure
       !LSUP  if JB < 1:  address of the new supporting bank
              if JB = 1:  the new supporting link*
          JB  if JB < 1:  link bias in the new supporting bank
              if JB = 1:  LSUP is the new supporting link,
                           the origin-link in the bank at LSH
                           will be made to point to it
              if JB = 2:  detach without insertion
       IFLAG  if IFLAG = 0:  shunt the one single bank at LSH
              if IFLAG = 1:  shunt the whole linear structure
                              pointed to by LSH

If the bank or the structure to be re-linked is in fact inserted or added into an existing linear structure, both must be contained in the same division.

Examples:

Suppose we have the following data-structures to start with:

       ______
      |      |                         up
      |  UA  | <---.-------------.-------------.
      |______|     |             |             |
         |         |             |             |
      -3 |         |             |             |
         |       ______        ______        ______
         |      |      |  <-- |      |  <-- |      |
         `----> |  A1  | ---> |  A2  | ---> |  A3  |
                |______|      |______|      |______|
and
       ______
      |      |                              up
      |  UN  | <---.-------------.-------------.
      |______|     |             |             |
         |         |             |             |
      -7 |         |             |             |
         |       ______        ______        ______
         |      |      |  <-- |      |  <-- |      |
         `----> |  N1  | ---> |  N2  | ---> |  N3  |
                |______|      |______|      |______|
and
                    ______        ______        ______
              <--- |      |  <-- |      |  <-- |      |
      LQMAIN  ---> |  X1  | ---> |  X2  | ---> |  X3  |
                   |______|      |______|      |______|

Any bank may support further dependent partial data-structures, the corresponding structural down-links are not changed by ZSHUNT.

In what follows the notation Lxx is used to designate a link pointing to bank xx.

Examples:

         CALL ZSHUNT (0,LA2,LUN,-7,0)     gives:
       ______
      |      |
      |  UA  | <---.-------------.
      |______|     |             |
         |         |             |
      -3 |         |             |
         |       ______        ______
         |      |      |  <-- |      |
         `----> |  A1  | ---> |  A3  |
                |______|      |______|
and
       ______
      |      |
      |  UN  | <---.-------------.-------------.-------------.
      |______|     |             |             |             |
         |         |             |             |             |
      -7 |         |             |             |             |
         |       ______        ______        ______        ______
         |      |      |  <-- |      |  <-- |      |  <-- |      |
         `----> |  A2  | ---> |  N1  | ---> |  N2  | ---> |  N3  |
                |______|      |______|      |______|      |______|

This moves a single bank (with is dependents, if any) out of a linear structure, and inserts it at the head of the linear structure supported by link -7 of the bank UN.

         CALL ZSHUNT (0,LA2,LUN,-7,1)     gives:
       ______
      |      |
      |  UA  |
      |______|
         |
      -3 |
         |       ______
         |      |      |
         `----> |  A1  |
                |______|
and
  ______
 |      |
 |  UN  |  <--.-------------.-------------.------------------.
 |______|     |             |             |                  |
    |         |             |             |                  |
 -7 |         |             |             |                  |
    |       ______        ______        ______             ______
    |      |      |  <-- |      |  <-- |      |  <- ... - |      |
    `----> |  A2  | ---> |  A3  | ---> |  N1  | -- ... -> |  N3  |
           |______|      |______|      |______|           |______|

This is the same as example 1, except that the (partial) linear structure starting with bank A2 is re-linked.

         CALL ZSHUNT (0,LA2,LN2,0,0)      gives:
       ______
      |      |
      |  UN  | <---.-------------.-------------.-------------.
      |______|     |             |             |             |
         |         |             |             |             |
      -7 |         |             |             |             |
         |       ______        ______        ______        ______
         |      |      |  <-- |      |  <-- |      |  <-- |      |
         `----> |  N1  | ---> |  N2  | ---> |  A2  | ---> |  N3  |
                |______|      |______|      |______|      |______|

This is again like example 1, but the bank is inserted inside the linear structure, rather than ahead of it.

          CALL ZSHUNT (0,LA2,LQMAIN,1,0)   gives:
                  0             0             0             0
                  ^             ^             ^             ^
                  |             |             |             |
                 ______        ______        ______        ______
         <----  |      |  <-- |      |  <-- |      |  <-- |      |
  LQMAIN -----> |  A2  | ---> |  X1  | ---> |  X2  | ---> |  X3  |
                |______|      |______|      |______|      |______|

This relinks bank A2 to be the first in the top-level linear structure supported by LQMAIN.

          L = LQMAIN
          CALL ZSHUNT (0,LA2,L,1,0)

has exactly the same effect as Example 4 above because, LQMAIN not being zero initially, the origin-link of the bank pointed to by L (and the up-link, but this is zero) is used for the connection.

         CALL ZSHUNT (0,LA1,LHEAD,1,1)     gives:
       ______
      |      |
      |  UA  |
      |______|
         |
      -3 |
         |
          ----> zero
and               0             0             0
                  ^             ^             ^
                  |             |             |
                 ______        ______        ______
         <----  |      |  <-- |      |  <-- |      |
  LHEAD  -----> |  A1  | ---> |  A2  | ---> |  A3  |
                |______|      |______|      |______|

supposing LHEAD=0 initially; this connects the linear structure to the (structural) link LHEAD, ie. the origin-link of the header bank A1 points back to the location of LHEAD.

         CALL ZSHUNT (0,LA1,LDUMMY,2,1)    gives:
       ______
      |      |
      |  UA  |
      |______|
         |
      -3 |
         |
         `----> zero
and               0             0             0
                  ^             ^             ^
                  |             |             |
                 ______        ______        ______
         0 <--  |      |  <-- |      |  <-- |      |
  LA1    -----> |  A1  | ---> |  A2  | ---> |  A3  |
                |______|      |______|      |______|

This detaches the linear structure from its old context without inserting it into a new one. This should only be temporary, one should insert the floating structure into a new context by a second call to ZSHUNT not too much later.


ZTOPSY et al. - Utilities to operate on a linear structure

These routines perform service operations on linear structures. The parameter LLS is the address of the first bank of the linear structure.

                      +----------------------------+
                      |CALL ZTOPSY (IXSTOR,*!LLS*) |
                      +----------------------------+
                                  

reverses the order of the banks in the linear structure, ie. the first bank becomes the last, and the last the first, for walking through the structure with L=LQ(L). Starting with Zebra version 3.67, LLS is updated to point to the first bank of the inverted structure on return.

                       +--------------------------+
                       |CALL ZPRESS (IXSTOR,!LLS) |
                       +--------------------------+
                                  

removes by bridging dead banks still present in the linear structure pointed to by LLS.


ZSORT - Utility to sort the banks of a linear structure

These routines re-arrange the horizontal linking within a given linear structure such that the key-words contained in each bank increase monotonically when moving through the linear structure with L=LQ(L). For equal key-words the original order is preserved.

Key-words may be either floating, integer or Hollerith. For Hollerith sorting a collating sequence inherent in the representation is used, thus the results will depend on the machine.

Sorting may be done either for a single key-word in every bank or for a key vector in every bank:

                    +--------------------------------+
                    |CALL ZSORT (IXSTOR,*!LLS*,JKEY) |
                    +--------------------------------+
                                  

Sorts banks according to a single floating-point keyword

                   +----------------------------------+
                   | CALL ZSORTI (IXSTOR,*!LLS*,JKEY) |
                   +----------------------------------+
                                  

Sorts banks according to a single integer keyword

                   +----------------------------------+
                   | CALL ZSORTH (IXSTOR,*!LLS*,JKEY) |
                   +----------------------------------+
                                  

Sorts banks according to a single Hollerith keyword

                 +--------------------------------------+
                 |CALL ZSORV (IXSTOR,*!LLS*,JKEY,NKEYS) |
                 +--------------------------------------+
                                  

Sorts banks according to a floating-point key vector

                +----------------------------------------+
                | CALL ZSORVI (IXSTOR,*!LLS*,JKEY,NKEYS) |
                +----------------------------------------+
                                  

Sorts banks according to an integer key vector

                +----------------------------------------+
                | CALL ZSORVH (IXSTOR,*!LLS*,JKEY,NKEYS) |
                +----------------------------------------+
                                  

Sorts banks according to a Hollerith key vector

    with the parameters
      IXSTOR  index of the store or of any division in this store,
              zero for the primary store;
      *!LLS*  the address of the first bank of the linear structure,
              reset on return to point to the new first bank;
        JKEY  in each bank at L, Q(L+JKEY) is the key word,
                                 or the first word of the key vector;
       NKEYS  the number of words in the key vector.

The execution time taken by these routines is a function of the re-ordering which needs to be done. For perfect order the operation is a simple verification pass through the structure. The maximum time is taken if the banks are initially arranged with decreasing key words.

Sorting re-links the banks such that the key-words are in increasing order. If one needs them in decreasing order on may use CALL ZTOPSY (IXSTOR,LLS) which reverses the order of the banks in the linear structure pointed to be LLS.


MZLINK/MZLINT - Initialise a link area

A link area is a vector of links outside any dynamic store, with all its links pointing to one particular store, consisting of NS structural links followed by NR reference links. Either NS or NR may be zero.

We distinguish two kinds of link areas:

Permanent link area

A permanent link area is initialized once at the steering level and stays alive for the whole program; it consists of just the vector of links.

Temporary link area

A temporary link area is requested and de-activated by lower-level code any number of times. Each such area has two words pre-fixed to the link-vector for efficiency:

word 1
is a flag indicating whether the area is active or not; if this word is zero, the contents of the area will not be updated in case of a memory move. The user may reset this word to zero to de-activate the area.
word 2
is a key allowing the system to easily find the where-abouts of this area in the system tables without searching. This word must never be touched by the user.

A link area must be in COMMON storage; if it is in local storage there is a danger that Fortran optimization causes a register to preserve the old value of a link across a relocation operation, for garbage collection, but also for simple updating with MZDROP, ZSHUNT, etc.

As for links in banks, a structural link may only contain zero or the valid address of a bank; it must never contain garbage.

To initialize a permanent link area, one calls once, and once only for this area:

            +------------------------------------------------+
            |CALL MZLINK (IXSTOR,chNAME,!LAREA,!LREF,!LREFL) |
            +------------------------------------------------+
                                  

with

      IXSTOR  index of the store into which the links will point,
              (IXDIV of any division in this store allowed)
              zero for the primary store
      chNAME  name of the Fortran Common in which the link area resides,
              for printing purposes, 8 characters maximum
      !LAREA  first word of the link area,
              being also the first link of this area
       !LREF  first reference link, if any;
              last structural link, if no reference links
      !LREFL  last reference link, if any,
              if none: give LAREA in this parameter position
 
MZLINK will clear the links to zero. Examples:
mixed link area:
      COMMON /LAMIX/ LS1,...,LSN, LR1,...,LRN
      CALL MZLINK (0,'/LAMIX/',LS1,LR1,LRN)
structural link area:
      COMMON /LASTR/ LS1, ..., LSN
      CALL MZLINK (0,'/LASTR/',LS1,LSN,LS1)
reference link area:
      COMMON /LAREF/ LR1, ..., LRN
      CALL MZLINK (0,'/LAREF/',LR1,LR1,LRN)

Note that in a permanent link area with exactly one link this link cannot be a reference link.

MZLINK prints at log level zero (or above) an initialization message on unit IQLOG.

To activate a temporary link area, one calls with:

            +------------------------------------------------+
            | CALL MZLINT (IXSTOR,chNAME,LAREA,!LREF,!LREFL) |
            +------------------------------------------------+
                                  

with

      IXSTOR  index of the store, as for MZLINK
      chNAME  name of the link area, as for MZLINK
       LAREA  first word of the link area,
              with: LAREA(1) the user flag word
                     LAREA(2) system word
                     LAREA(3) the first link of the area
       !LREF  first reference link, if any, as for MZLINK
      !LREFL  last reference link, if any, as for MZLINK
 
MZLINT will clear the links to zero, set the flag-word LAREA(1) to be non-zero, and set the system-word LAREA(2) on first contact. To de-activate a temporary link-area the user sets LAREA(1)=0. From then on the links in this area are no longer relocated, and hence will be meaningless. To re-activate the area the user could set LAREA(1)=1, but he must then clear the contents of the links himself; it is safer to call MZLINT, which will do the necessary. Examples:
mixed link area:
      COMMON /LAMIX/ LAMIX(2), LS1,...,LSN, LR1,...,LRN
      CALL MZLINT (0,'/LAMIX/',LAMIX,LR1,LRN)
structural link area:
      COMMON /LASTR/ LASTR(2), LS1, ..., LSN
      CALL MZLINT (0,'/LASTR/',LASTR,LSN,LASTR)
reference link area:
      COMMON /LAREF/ LAREF(2), LR1, ..., LRN
      CALL MZLINT (0,'/LAREF/',LAREF,LR1,LRN)
 
MZLINT prints a log message at level 1 for initialization and at level 2 for re-initialization.

MZBOOK/MZLIFT - Bank creation

       +----------------------------------------------------------+
       |CALL MZBOOK (IXDIV,!L*,!LSUP,JB, chID,NL,NS,ND,IOD,NZERO) |
       +----------------------------------------------------------+
                                  

or

             +----------------------------------------------+
             |CALL MZLIFT (IXDIV,!L*,!LSUP,JB, NAME, NZERO) |
             +----------------------------------------------+
                                  

with

        IXDIV   index of the division in which the bank is to be lifted
                 = 0       use default in   primary store
                [= IXSTOR  use default in secondary store ]
                see below for details
          !L*   returns the address of the bank created
        !LSUP   if JB < 0:  address of the supporting up bank
                if JB = 0:  address of the supporting previous bank
                if JB = 1:  supporting link*
                if JB = 2:  LSUP not used
           JB   if JB < 1:  link bias in the supporting bank
                             (must be a structural link)
                if JB = 1:  create top-level bank
                if JB = 2:  create stand-alone bank
         NAME   specifies the properties of the bank:
 NAME(1) = ID:  4-character bank identifier, alphanumeric,
                  MZBOOK: variable of type CHARACTER
                  MZLIFT: Hollerith string of the form 4Hxxxx
     (2) = NL:  total number of links, NL < 64k
     (3) = NS:  number of structural links, NS <= NL
                  (not counting the 3 links next, up, origin)
     (4) = ND:  number of data words, ND < 1 Mwords
     (5) = IOD:  I/O descriptor for the data words, which may be:
                 - result from MZFORM, 1 word, I/O index
                 - result from MZIOBK, 1 or more words, I/O characteristic
                 - immediate, the whole bank is of the same type:
                    IOD = 0  undefined, bank cannot be transported
                          1  32-bit unsigned integer
                          2  signed integer
                          3  floating         4  double-precision
                          5  Hollerith        7  self-descriptive
                 - requests:
                    IOD = 9  retrieve I/O characteristic by IDH,
                             stored in the system by MZFORM
                         11  take the I/O characteristic from any
                             bank in the target linear structure,
                             if this is empty act as for 9

        NZERO   controls whether and how much of the data-part
                of the bank is preset to zero:
                   N = -1:  no presetting
                   N = 0:  the whole bank is cleared
                   N > 0:  the first N words are cleared
                The links of the bank are always cleared to zero.

In what follows we will need the parameter LNEXT, which is the address of the successor bank to the bank to be lifted; its address will be stored into link 0 of the new bank, ie. into its 'next' link. LNEXT is obtained as follows:

        if  JB < 1:   LNEXT = LQ(LSUP+JB)
            JB = 1:   LNEXT = LSUP
            JB = 2:   LNEXT = 0

The division [ and the store ] into which the bank is to be lifted is selected by the parameter IXDIV. If a definite division is specified, this is used, but it must be compatible with LNEXT; a linear structure must be contained within one and the same division ! If 'default division' is specified, by giving zero [ or IXSTOR ] for IXDIV, the bank will be created in the division holding the logically nearest bank of the data structure into which the new bank will be inserted, in this order of priority:

  1. if the bank is added to an existing, non-empty, linear structure, i.e. if LNEXT is non-zero, or if JB=0, use the division holding this linear structure;
  2. if JB < 0 use the division holding the supporting up bank at LSUP;
  3. use division 2.

The numeric bank identifier IDN serves to readily identify a particular bank within a linear structure for interactive work. Normally the Hollerith identifiers IDH in a linear structure are all identical; it is desirable that the IDN's are all different. In principle the user can set any positive integer into IQ(L-5) after the bank has been created, but MZLIFT/ MZBOOK will store a default value in this order of priority:

  -  if LNEXT is non-zero: use IDN+1 of the bank at LNEXT;
  -  if JB = 0: use IDN+1 of the bank at LSUP;
  -  if JB < 0: use the value IDN = -JB;
  -  IDN = 1 will be set.

The bank being lifted is inserted into an existing data-structure according to LSUP under control of the parameter JB:

--- case 1: JB < 0       insert as dependent of the bank at LSUP
                          ex.:  CALL MZLIFT (0,L,LSUP,-4,NAME,0)
            _______
           |       |
         __|  SUP  |  <-------------------------.------ ...
        |  |       |  <---------.               |
  link  |  |_______|            |               |
JB= -4  |                       |               |
        |                       |               |
        |                    up |            up |
        |                  ________        ________
        |                 |        |      |        |
        `---------------> |  lift  | ---> |  NEXT  | --->  ...
                          |________|      |________|
  SUP   is the supporting bank whose address is LSUP
  lift  is the new bank being lifted
  NEXT  is the first bank of the linear structure in front of which
        the bank 'lift' is inserted, ie. the initial content of
        link JB in bank SUP was LNEXT = LQ(LSUP+JB).
The up-link and the origin-link in the bank lifted are set as
                  up-link  <--  LSUP
              origin-link  <--  LSUP+JB
--- case 2: JB = 0       insert inside a linear structure
                          ex.:  CALL MZLIFT (0,L,LSUP,0,NAME,0)
     ______
    |      |
  __|  UP  |  <------------------------------------------.---- ...
 |  |      |  <------------.                             |
 |  |______|               |                             |
 |                         |<-------------.              |
 |                         |              |              |
 |                      up |           up |           up |
 |      ____          ________       ________       ________
 |     |    |        |        |     |        |     |        |
 `---> |    | -...-> |  SUP   | --> |  lift  | --> |  NEXT  | --> ...
       |____|        |________|     |________|     |________|
The up-link and the origin-link in the bank lifted are set as
                  up-link  <--  copy of the up-link in bank SUP
              origin-link  <--  LSUP

SUP, lift, NEXT have the same significance as in case 1. UP is the bank pointed to by the up-link in bank SUP, which link might be zero. If bank NEXT does not exist, ie. if LNEXT = LQ(LSUP) is zero, the bank 'lift' is added at the end of the linear structure.

--- case 3: JB = +1      create new top-level bank
                          ex.:  CALL MZLIFT (0,L,LSUP,1,NAME,0)
              usually: zero  <------------------.
                                                 |
                                                 |
                                  -------------->|
                                 |               |
                              up |            up |
                            ________        ________
                           |        |      |        |
 result  LSUP and L  ----> |  lift  | ---> |  NEXT  | ---> ...
                           |________| .--> |________|
                                      |
  input  LSUP  -----------------------^

For JB=+1 the origin-link and the up-link are taken from the bank NEXT pointed to by LSUP on input; the first time round, when NEXT does not exist, ie. when LSUP=0, the up-link is set to zero and the origin-link is made to point to the location containing LSUP.

The location pointed to by the origin-link is filled with the address of the bank created, unless the origin-link is zero.

--- case 4: JB = +2      no inserting at all
                          ex.:  CALL MZLIFT (0,L,0,2,NAME,0)

For JB=2 the bank is lifted without being linked into a structure; both the origin-link and the up-link in the bank are set to zero.

Note:

because structural links are used to connect banks as illustrated by these examples, and are hence manipulated by the system, they must never contain anything but valid bank addresses, or zero.

MZLIFT and MZBOOK print a monitor log message at level 2.


MZPUSH - Alter the size of a bank

To increase or decrease the size of a bank, one may

              +--------------------------------------------+
              | CALL MZPUSH (IXDIV,*!L*,INCNL,INCND,chOPT) |
              +--------------------------------------------+
                                  

with

       IXDIV  index of the division; zero [or IXSTOR] allowed,
              in which case MZPUSH will find the division
        *!L*  address of the bank to be pushed,
              on return L contains the new address of the bank
              (cannot be a link in a bank !)
       INCNL  number of additional links,
              zero for no change, negative for decrease.
              Additional links will be given type 'reference',
              unless the original bank has only structural links.
       INCND  number of additional data words,
              zero for no change, negative for decrease.
       chOPT  character string of options:
                 default: any link may point to the bank at L,
              R  but not into the abandoned bank region
                        (in case of bank reduction)
              I  isolated: only the inherent structural links
                           point to this bank

For 'isolated' the user is sure that no link other than the supporting structural link, and the link passed in the parameter L, and the reverse links in the first level dependents, point to this bank.

The I/O descriptor of the old bank must also be valid for the new bank; if this would not be the case one should use MZREPL instead of MZPUSH.

New link words and new data words acquired by the bank are cleared to zero.

Except for special cases, increasing the size of a bank is an expensive operation, and should be avoided. The correct solutions to the problems of indefinite number of objects or of indefinite size are either the use of a linear structure or the lifting of a bank of maximum size, which is reduced to the final size once the contents are complete.

The increase of a bank is handled in general as follows: a new bank is lifted with the new dimensions, the contents of the original bank are copied over, and the old bank is marked as dropped. Any link pointing to the original bank must be re-routed to the replacement bank by MZPUSH. In full generality this can only be done by a relocation pass over all memory (ie. all link areas and all divisions pointing to the division in which the bank resides.)

The expensive part in this operation is the relocation pass, and this can be saved under special circumstances:

a) Increasing the data part of the last bank in a forward division, or the link part in a reverse division, can be done in situ without the lifting of a new bank. Hence any link pointing to the bank remains valid. (However note: this is not a good reason for creating an extra division, because the lifting of a maximum size bank is a better solution.)

b) If there are no links pointing to the bank (except the standard structural support links) there is no point to scan all links, because the relocation pass would have no effect. However, ZEBRA could not detect this situation without actually performing the relocation pass, but the user may know the situation a priori, in which case he can signal it to MZPUSH with the 'I' option.

Reducing the size of a bank is less onerous (if the 'R' option is given). The parameters of the original bank are adjusted to the new dimension in situ and the abandoned memory is marked as dead. Links which point into the abandoned region, if any, can only be reference links and must be reset to zero. To have links pointing into a bank, rather than to the status-word, is relatively rare. Again, the relocation pass can be saved, and for reducing a bank this is the normal situation, if the user knows the situation a priori and signals it to MZPUSH with the 'R' option (or 'I' which is stronger).

MZPUSH prints a monitor log message at level 2, but if the operation involves a relocation pass a log message will be given at level 1.

MZPUSH accumulates statistics of the number of times that an expensive operation was performed; this is printed by MZEND.


MZDROP - Drop a bank and its dependents

With MZDROP one may either drop the bank at L or, if the 'L' option is given, the linear structure at L. Dropping a bank implies dropping also the whole partial data-structure which depends on this bank downwards. Dropped banks stay in memory with the IQDROP status-bit set, links pointing to them continue to do so, except for the immediate structural link indicated via the origin-link of the bank at L, which is bridged or reset to zero, whichever is appropriate. To drop one gives
                    +--------------------------------+
                    |CALL MZDROP (IXSTOR, !L, chOPT) |
                    +--------------------------------+
                                  

with

      IXSTOR  index of the store or of any division in this store,
              zero for the primary store
          !L  the address of the bank or linear structure to be dropped
       chOPT  character string of options:
                  default: drop the bank at L only,
                  ie. the 'next' link of this bank is not followed
               L  drop the linear structure pointed to by L
                  ie. the 'next' link of the bank at L is followed
               V  drop only the partial data-structure
                  dependent vertically downwards from the bank at L,
                  but not the bank itself.
 
MZDROP prints a monitor log message at level 2. The parameter L in the call to MZDROP is not as such changed on return, but if it is the structural link supporting the d/s it will in fact be up-dated for the removal of the first bank in this structure. Suppose this artifical sequence of operations (with LS=0 initially):
      CALL MZLIFT (0,L,LS,1,...)          create bank '3'
      CALL MZLIFT (0,L,LS,1,...)          create bank '2'
                                  LS now points to bank 2
      CALL MZLIFT (0,L,LS,1,...)          create bank '1'
                                  LS now points to bank 1
      CALL MZDROP (0,LS,'.')              drop bank 1
                                  LS now points again to bank 2
if however the dropping is done with
      CALL MZDROP (0,L,'.')               drop bank 1
         then LS will be made to point to bank 2,
         but L will continue to point to the (dead) bank 1.

Since the parameter L in the call to MZDROP is not changed, selective dropping of banks in a linear structure can be done with a loop like this:

      L = start adr of the linear structure
   12 IF (L.NE.0)  THEN
          IF (bank not wanted)  CALL MZDROP (0,L,'.')
          L = LQ(L)
          GO TO 12
        ENDIF


MZWIPE - Reset a complete division to empty

By calling MZWIPE the user can delete the contents of whole divisions:

                         +----------------------+
                         | CALL MZWIPE (IXWIPE) |
                         +----------------------+
                                  

with the parameter IXWIPE indicating the divisions of a particular store to be wiped out. IXWIPE may be any of the three possible forms of a division index:

  a) specific division index, as returned by MZDIV
  b) generic division index, [ IXSTOR + ] n, where
        n = 21:  all user short range divisions
            22:  all user long  range divisions
            23:  all package divisions
  c) compound division index, as created by MZIXCO, see there for details.

IXWIPE = 0 is taken to mean IXWIPE = 21, ie. all short range divisions in the primary store; similarily IXSTOR+21 can be used.

Wiping divisions resets the divisions to be empty, but without reducing the space reserved for them, followed by a relocation pass to reset to zero all links pointing into the wiped divisions. Included into this pass are the links of all link areas, of the working space and of all divisions which are declared to point to the divisions in question (all of this for one particular store only, of course).

If several divisions are to be wiped out, this must be done by one call to MZWIPE, and not by several calls in succession, to save the time of multiple relocation passes, each of which would take longer than the single pass.

Examples:

for the primary store one has:

  1) Wipe the last 'event':      CALL MZWIPE (0)
  2) Wipe division 1:            CALL MZWIPE (1)
  3) Wipe all user's divisions:  IX = MZIXCO (21,22,0,0)
                                  CALL MZWIPE (IX)
  4) Wipe divisions IX1, IX2, IX3, IX4, and IX5:
                   IX = MZIXCO (IX1,IX2,IX3,IX4)
                   IX = MZIXCO (IX,IX5,0,0)
                   CALL MZWIPE (IX)
 
MZWIPE operates by calling MZGARB, which will print a monitor log message at level 2.

MZGARB - Garbage collection requested by the user

Garbage collection is triggered by the system if there is not enough space to satisfy an MZWORK or MZLIFT request (hopefully this does not normally occur). Thus the user does not have to worry about initiating garbage collection to win space in the dynamic store. To remove the last event from the store, the user calls MZWIPE which is much more efficient than dropping the banks of the event followed by garbage collection. He may however occasionally want to force a garbage collection to tidy up his data, particularly during the initialization phase of his program. Again, as in MZWIPE, if there are several divisions to be tidied up, this must be done by one call to MZGARB. Also, if one or several divisions are to be wiped out at the same moment, this should be included into the call to MZGARB; one and the same relocation pass can handle wiping and garbage collection simultaneously. The calling sequence is:
                      +----------------------------+
                      |CALL MZGARB (IXGARB,IXWIPE) |
                      +----------------------------+
                                  

with

      IXGARB  index of the divisions where garbage
              is to be collected (none if =0)
      IXWIPE  index of the divisions to be wiped out
              = 0:  no divisions to be wiped

Both IXGARB or IXWIPE may be any of the three possible forms of a division index:

  a) specific division index, as returned by MZDIV
  b) generic division index, [ IXSTOR + ] n, where
        n = 21:  all user short range divisions
            22:  all user long  range divisions
            23:  all package divisions
  c) compound division index, as created by MZIXCO, see there for details.
 
MZGARB prints a monitor log message at level 2, but if the operation involves a garbage collection a message is given at level 1.

MZFORM et al. - Handle the I/O characteristic

MZFORM may cause garbage collection.

The nature of the contents of any bank which is to be transported from one computer to another one has to be indicated to ZEBRA, such that it can do the necessary tranformations. In the simplest case that all the data words of a bank are of the same type, this is easily indicated in the parameters to MZLIFT or MZBOOK. For anything more complicated the user specifies the "format" of the bank by calling MZIOBK or MZFORM which encode the format into a variable number of words to be included into each bank in the system part as the "I/O characteristic".

Thus the content description is carried by each bank; this avoids complicated logistics of finding bank descriptors elsewhere than in the bank itself. Complex bank contents require a relatively large number of extra system words. This could represent a substantial overhead on memory or file space occupation, which the user can avoid in the design of his bank contents. Anyway, the number of these extra descriptor words is limited to 16, and any descriptor which would need more is refused. Thus ZEBRA will not handle any arbitrary bank contents via this basic procedure, but by using the concept of the "self-describing" sector (see below) the user can indeed store any kind of information mix, decided at execution time, into a bank and have it travel from one computer to another one.

Sectors

The basic element for setting up an I/O characteristic is the sector, which is a number of words consecutive in the bank which are all of the same type. A sector is described in the format parameter to MZFORM et al. as a combination of its word-count "c" and its type "t" as "ct". For example, 24F is a sector of 24 single-precision floating-point numbers, 24D is a sector of 24 words holding 12 double-precision numbers, and 1I is a sector of one integer word.

The possible values for "t" are:

      t =  B   bit string of 32 bits, right justified
           I   integer
           F   floating-point
           D   double precision
           H   4-character Hollerith
           S   self-describing sector (see below)

A ``static'' sector is a sector of a fixed number of words, such as the 24F of the example above.

An ``indefinite-length'' sector is a sector whose end is defined by the end of the bank. This is written as -t, for example -F signals that the rest of the bank is all floating-point words.

A ``dynamic'' sector is a sector which is preceded in the bank by a single positive integer word indicating the sector length; if this number is zero this means that the rest of the bank is currently unused. This is written as *t, for example *F indicates a dynamic sector of type floating.

Thus the word-count "c" in the sector specification is written as:

      c =  n   numeric, n words:       static length sector
           -   all remaining words:    indefinite length sector
           *   dynamic length sector

A ``self-describing'' sector is a dynamic sector whose type is also encoded into the one word preceding the sector as

   word 0  =  16*NW + IT
      with  NW = length of the sector
            IT = numeric representation of the type
                 =  1  B bit string    2  I integer
                    3  F floating      4  D double precision
                    5  H Hollerith
                    6  (reserve)       7  (special)

The form "nS" is meaningless; the form "*S" indicates one particular sector; the form "-S" is special in that it indicates that the rest of the bank is filled by self-describing sectors, as many as there may be. (Thus the forms, for example, '4I 5F / *S' and '4I 5F --S' are equivalent, but the second form is more economic; the user may give either, the internal result will be the second form.)

Complete Characteristics

Looking now at the bank as a whole, we divide it into a "leading part" and a "trailing part", either of which may be empty.

The leading part consists of one region of maybe several sectors, occurring once at the beginning of the bank. This leading region may end with an indefinite-length sector, in which case the trailing part is empty.

The trailing part of the bank may be empty or it may consist of an indefinite number of regions which all have the same structure, such that the same format description is valid for all of them.

The symbol "/" marks the break between the leading region and the trailing regions in the format parameter to MZFORM et al.

Examples:

trailing part empty:
      '-F'         the whole bank is floating
      '3I -F'      the first 3 words are integer, the rest is F
      '*I -F'      the first word n=IQ(L+1) is a positive integer,
                   words 2 to n+1 are integers, the rest is F
      '3B *I -F'   the first sector consists of 3 words bit-string,
                   the second sector is dynamic of type integer,
                   the rest of the bank is floating
      '3I *F'      the first 3 words are integer, followed by a
                   dynamic sector of type F, the rest (if any) of
                   the bank is currently unused

both parts present
      '3B 7I / 2I 4F 16D'  the leading region has 3 B and 7 I words,
                           each trailing region consists of 2 integer
                           words, followed by 4 F words, followed
                           by 16 D words, ie. 8 double-precision numbers
      '4I / *H'    the bank starts with 4 integer words,
                   the rest is filled with dynamic Hollerith sectors
      '*I / 2I *F' the leading region is one dynamic I sector,
                   each trailing region consists of 2 integers
                   followed by a dynamic F sector
                   (ie. 3 integers plus a number of floating words
                    this number being indicated by the 3rd integer)
leading part empty
      '/ *H'       the bank is filled with dynamic Hollerith sectors
      '/ 4I 29F'   4 integers and 29 floating numbers alternate

Economic formats

It is in the interest of the user to design his bank contents such that the I/O characteristic is as simple as possible, because the number of system words in any bank increases with the complexity of the lay-out. "Simple" means: as few sectors to be described as possible.

For example:  '2B 2I 2B 2I 2B 2I -F' is much less simple then
               '6B 6I -F'.

Moreover, if the integers described by this format are sure to be positive integers, then one can use the even simpler form '12B -F'.

In the following we give an exhaustive list of the most economic bank formats, those requiring zero or one extra system word in the banks.

Zero extra I/O words

These bank formats can be described by the 16 bits of the I/O control-byte alone:

   (0)  '-t' or '*t'
        'ct -t'           if c < 64   (c=* is represented as c=0,
          or 'ct *t'                   hence  '*t -t'  is a sub-case)
   (1)  '*t *t -t'
   (2)  '*t *t *t'
   (3)  'ct / *t'         if c < 64
        '/ ct *t'         if c < 64
        '/ ct'            this is useful only if c=*
                          else the form '-t' is used
   (4)  '*t / *t *t'
        '*t *t / *t'
   (5)  '/ *t *t *t'

One extra I/O word

These bank formats can be described by the 16 bits of the I/O control-byte plus the 32 bits of one extra I/O word:

   (1)  'ct -t'
        'ct ct -t'        if c < 65536
        'ct ct ct -t'     if c < 1024
   (2)  'ct *t'
        'ct ct *t'        if c < 65536
        'ct ct ct *t'     if c < 1024
   (4)  'ct / ct'         if c < 65536
        'ct / ct ct'      if c < 1024
        'ct / ct ct ct'   if c < 256
        'ct ct / ct'      if c < 1024
        'ct ct / ct ct'   if c < 256
   (5)  '/ ct ct'         if c < 65536
        '/ ct ct ct'      if c < 1024        (remember:
        '/ ct ct ct ct'   if c < 256          c=0 means c=*)

Three routines are provided to mediate between the user specifying the bank format in a readable form and the highly encoded I/O characteristic to be included into any bank at creation time.

MZIOCH

analyses the format chFORM to convert and pack it into the output vector IOWDS. This is the basic routine, but it is usually called by the user only to specify formats of objects other than banks, like the user header vector for FZOUT.

To specify bank formats the following two routines serve more conveniently:

MZIOBK

is provided for the context of MZLIFT; like MZIOCH it analyses the format chFORM, but it stores the result as part of the bank-description vector NAME for MZLIFT.

MZFORM

again analyses the format chFORM, but it does not return the result to the user. Instead, it remembers the I/O characteristic in a system data-structure, returning to the user only the index to the characteristic in the system. The user may then either pass this index to MZBOOK (or MZLIFT) at bank creation time, or alternatively he may request MZBOOK (or MZLIFT) to search the system data-structure for the I/O characteristic associated to the Hollerith identifier IDH of the bank to be created.

The first word of the I/O characteristic delivered by MZIOCH or MZIOBK has the following redundant format:

          |       16       |    5    |     5     | 6 bits|
          |----------------------------------------------|
          |  control-byte  |  NWXIO  |  NWXIO+1  |     1 |
          |----------------|---------|-----------|-------|

The I/O index delivered by MZFORM has the following format:

          |       16       |    5    |     5     | 6 bits|
          |----------------------------------------------|
          |      index     |    0    |  NWXIO+1  |     2 |
          |----------------|---------|-----------|-------|

where NWXIO is the number of extra I/O words, ie. the total length of the characteristic is NWXIO+1.

Typing rules for chFORM

The format should be typed giving the "ct" for each sector, in the order in which they occur in the bank, as shown in the examples. Leading, interspersed, and trailing blanks (also comma or dot) for aeration are allowed and ignored.

Single-word sectors must be typed as '1t', 't' alone is illegal.

The c for double-precision sectors gives the number of words, thus 14D specifies 7 double-precision numbers; 7D is illegal.

                   +----------------------------------+
                   | CALL MZIOCH (IOWDS*,NWIO,chFORM) |
                   +----------------------------------+
                                  

with

           IOWDS*  the I/O words to receive the result,
                   a vector dimensioned to NWIO
             NWIO  the maximum size of IOWDS, < 17
           chFORM  the format as a CHARACTER string

                    +--------------------------------+
                    |CALL MZIOBK (NAME*,NWMM,chFORM) |
                    +--------------------------------+
                                  

with

            NAME*  the bank description vector for MZLIFT,
                   the resulting characteristic will be stored
                   into the I/O words starting at NAME(5),
                   the IDH contained in NAME(1) will be used
                   if diagnostics are necessary,
                   a vector dimensioned to NWMM
             NWMM  the maximum size of NAME, < 21
           chFORM  the format as a CHARACTER string

                   +----------------------------------+
                   | CAL  MZFORM (chIDH,chFORM,IXIO*) |
                   +----------------------------------+
                                  

with

            chIDH  the Hollerith IDH of the bank, type CHARACTER
           chFORM  the format as a CHARACTER string
            IXIO*  returns the index to the characteristic stored
                   in a system data-structure,
                   this can be passed to MZBOOK/MZLIFT,
                   in which case it must not be modified

Examples:

      DIMENSION    IOHEAD(4), MMLIFT(8)
      CALL MZIOCH (IOHEAD,4, '8I -F')             for an FZIN user header
      CALL MZIOBK (MMLIFT,8, '2I / 2I 8F')        for MZLIFT
      CALL MZFORM ('RCBC', '2I/2I 8F'), IORCBC)   for reference by index

People creating data outside Zebra, but destined to be read by FZ of Zebra, will have to know the representation of the I/O characteristic stored into any bank:

The physically first word of any bank contains:

   right half:  NOFF = NIO + NL + 12
   (bits 1-16)   where  NIO: the number of extra
                              I/O descriptor words for the bank
                        NL: the number of links in the bank
   left half:   the I/O control byte, which controls the
   (bits 17-32)  interpretation of the I/O characteristic

In the simplest cases the I/O control byte alone specifies the nature of the data in the bank, without needing extra descriptor words (in which case NIO is zero). We give here the translation of some of these cases:

       -B:  0001             *B:  0009
       -I:  0002             *I:  000A
       -F:  0003             *F:  000B
       -D:  0004             *D:  000C
       -S:  0007

For example: suppose one were to prepare a bank with two links and 4000 data words which are all un-signed 32-bit integer (type B), a bank which is to travel in link-less mode such that all standard links are zero:

   word 1   0001 000E     -B | NOFF = 14
        2   zero          link 2
        3   zero               1
        4   zero          link next
        5   zero               up
        6   zero               origin
        7   IDN           numeric ID
        8   IDH           Hollerith ID
        9   2             number of links
       10   1, say        number of structural links
       11   4000          number of data words
       12   zero          status word
                          bits 19-22 give NIO, here zero
       13   data word 1
            ...
     4012   data word 4000

Note that the status word contains NIO on bits 19--22 to allow Zebra to reach the start-of-bank.

It is impraticable to tabulate the translation of more complicated formats. There is a little program DIOCHAR to interactively take a format chFORM, translate it and display the result in hexadecimal. This is not yet properly installed on the CERN machines, but on the Apollo people at CERN can run it by giving the command /user/zoll/uty/diochar

The subroutine MZIOTC is provided to convert an encoded IO characteristic back into printable form. One may hand to this routine the address of a bank and receive its IO characteristic in a CHARACTER variable. Alternatively one may pass to it an integer array as delivered by MZIOCH for back-conversion to CHARACTER, for example the IO characteristic of a user-header vector read with FZIN.

                +----------------------------------------+
                |CALL MZIOTC (IXST, !L, NCHTR*, chIOTR*) |
                +----------------------------------------+
                                  

or

                +----------------------------------------+
                |CALL MZIOTC (IOWDS, 0, NCHTR*, chIOTR*) |
                +----------------------------------------+
                                  

with

             IXST  the index of the store holding the bank,
                          or of any of its divisions
               !L  the address of the bank
            IOWDS  the integer array with the encoded characteristic
                   (L must be zero in this case)
           NCHTR*  number of useful characters stored into chIOTR
                   = 0 if trouble
          chIOTR*  the CHARACTER variable to receive the characteristic

The routine returns zero in NCHTR if L is non-zero and not a valid bank address, or if chIOTR is not long enough.


ZEBRA Diagnostics

As with ZEBRA Routines this section has been shamelessly copied from the ZEBRA Reference Manual

MZDROP
MZGARB - MZGAR1
MZLIFT - MZBOOK
MZLINK - MZLINT
MZRELB - MZRELL


MZDROP

                     +------------------------------+
                     | CALL MZDROP (IXSTOR,L,CHOPT) |
                     +------------------------------+
                                  

ZFATAL termination

  case 1 :  invalid input parameter L
  case 2 :  LN = LQ(L) is invalid
The following numbers are given in /QUEST/
for cases 1 and 2 :
 IQUEST(11) = L
for case 2 :
 IQUEST(12) = LN
In either case the parameters of the 'bank' pointed to by the invalid link L or LN are found in the post-mortem dump of /MZCN/.

MZGARB - MZGAR1

                      +----------------------------+
                      |CALL MZGARB (IXGARB,IXWIPE) |
                      +----------------------------+
                                  

ZFATAL termination

  case 1 :  the store numbers of IXGARB and IXWIPE do not agree
The following numbers are given in /QUEST/
for case 1 :
 IQUEST(11) = store number of IXGARB
 IQUEST(12) = store number of IXWIPE
                             +--------------+
                             | CALL MZGAR1  |
                             +--------------+
                                  
This is called from various parts of the system to force an automatic garbage collection for lack of space; NQRESV contains the amount of space available, a negative value indicates how much space has still to be found.

ZFATAL termination

  case 1 :  not enough space can be found for a long range division
The following numbers are given in /QUEST/
for case 1 :
 IQUEST(11) = NQRESV, the amount of free space available,
                      a negative number in this context
 IQUEST(12) = current store number
 IQUEST(13) = current division number (which may not be meaningful)

ZTELL exit

      CALL ZTELL (99,1)   not enough space for a short-range division
                          IQUEST(11:13) as above

MZLIFT - MZBOOK

              +--------------------------------------------+
              |CALL MZLIFT (IXDIV,L*,LSUP,JB, NAME, NZERO) |
              +--------------------------------------------+
                                  
        +--------------------------------------------------------+
        | CALL MZBOOK (IXDIV,L*,LSUP,JB, IDH,NL,NS,ND,IOD,NZERO) |
        +--------------------------------------------------------+
                                  

ZFATAL termination

  case 1 :  faulty NAME parameters
  case 2 :  JB < 1 : LSUP does not point to a live bank
            JB = 1 : LSUP contains an invalid link
  case 3 :  JB < 0 : the bank at LSUP has less than -JB structural links
  case 4 :  LNEXT is invalid
  case 5 :  I/O characteristic for IDH does not exist
  case 6 :  I/O parameter NAME(5) = IOD(1) is invalid
  case 7 :  trying to lift the bank into a wrong division,
            ie. the predecessor or the successor bank, address LSAME,
            is not in the division selected by IXDIV.
  case 8 :  with JB=1 : trying to connect the new bank to the link
                        at LQ(LP) in bank-space
The following numbers are given in /QUEST/
for all cases :
 IQUEST(11) = input value of LSUP
       (12) = JB
       (13) = IDH  -  NAME(1)
       (14) = NL   -  NAME(2)
       (15) = NS   -  NAME(3)
       (16) = ND   -  NAME(4)
       (17) = IOD  -  NAME(5)
for case 4 :
 IQUEST(18) = LNEXT          if JB < 1 :  LNEXT=LQ(LSUP+JB)
                                   = 1 :  LNEXT=LSUP
for case 7 :
 IQUEST(18) = LSAME
for case 8 :
 IQUEST(18) = LP
The parameters of the bank at LCHK are found in the post-mortem dump of /MZCN/, where LCHK is LSUP for cases 2 and 3; for case 4 LCHK is LNEXT.

MZLINK - MZLINT

             +----------------------------------------------+
             | CALL MZLINK (IXSTOR,CHNAME,LAREA,LREF,LREFL) |
             +----------------------------------------------+
                                  
             +----------------------------------------------+
             | CALL MZLINT (IXSTOR,CHNAME,LAREA,LREF,LREFL) |
             +----------------------------------------------+
                                  

ZFATAL termination

  case 1 :  the parameters are inconsistent
  case 2 :  the new link-area overlaps with the table of some store
  case 3 :  the new link-area overlaps with some store
  case 4 :  the new link-area overlaps with a previously defined
            link-area for some store
The following numbers are given in /QUEST/
for cases 1 to 4 :
 IQUEST(11) = char 1:4
       (12) =      5:8 of the printing name of the new link-area
       (13) = absolute adr of parameter LAREA
       (14) = absolute adr of parameter LREF
       (15) = absolute adr of parameter LREFL
       (16) = number of structural links derived
       (17) = total number of links derived
for cases 2 to 4 :
 IQUEST(18) = serial number of the clashing store;
                = 0 for the primary store,
                = 1 for the first secondary store, etc.
       (19) = char 1:4
       (20) =      5:8 of the printing name of the clashing store
for case 4 :
 IQUEST(21) = char 1:4
       (22) =      5:8 of the printing name of the clashing link-area
       (23) = absolute adr of the start of the clashing link-area
       (24)   only for MZLINT : = initial content of LAREA(2)
Case 4 will happen with MZLINT if the user over-writes the system word in LAREA(2), in which case the link-area will appear to clash with itself.
                +----------------------------------------+
                |CALL MZMARK (IXSTOR,L,CHOPT,NID,IDLIST) |
                +----------------------------------------+
                                  
See MZFLAG

MZRELB - MZRELL

                            +-----------+
                            |CALL MZRELB|
                            +-----------+

This is the relocator for links in banks.

ZFATAL termination

case 1 : bank chaining clobbered case 2 : a structural link pointing into a dead area, and to be bridged, does not contain a valid bank address The following numbers are given in /QUEST/ the following numbers are given in /QUEST/ for case 1 : IQUEST(11) = LN, the start of the clobbered region for case 2 : IQUEST(11) = LS, the adr of the bank containing the invalid link IQUEST(12) = LW, the adr of the word containing the invalid link IQUEST(13) = the content of this link +-----------+ |CALL MZRELL| +-----------+ This is the relocator for links in Link Areas.

ZFATAL termination

case 1 : a structural link pointing into a dead area, and to be bridged, does not contain a valid bank address The following numbers are given in /QUEST/ for case 1 : IQUEST(11) = absolute adr of the first word of the link area IQUEST(12) = relative position within the link area of the link to be bridged IQUEST(13) = the content of this link IQUEST(14) = char 1:4 (15) = 5:8 of the printing name of the link area
Go Back to the Snoman Companion Top Page


Highest link so far: 37:-
ZEBRA: A Beginner's Guide ZEBRA: A Memory Manager Bank Data Words Bank Status Word Bank Links Bank Structural Links Vertical (Down) Links Horizontal Links The Origin Link Bank Reference Links Bank Information Stores and Divisions Store Full and Garbage Collection Event Relocation and Local Links Bank Format Lifting a Bank Dropping a Bank or Data Structure Bank Layout and Access ZEBRA Routines LZFIND,NZBANK et al. - Utilities to interrogate a linear structure LZHEAD - find the first bank of a linear chain ZSHUNT - Change structural relation ZTOPSY et al. - Utilities to operate on a linear structure ZSORT - Utility to sort the banks of a linear structure MZLINK/MZLINT - Initialise a link area MZBOOK/MZLIFT - Bank creation MZPUSH - Alter the size of a bank MZDROP - Drop a bank and its dependents MZWIPE - Reset a complete division to empty MZGARB - Garbage collection requested by the user MZFORM et al. - Handle the I/O characteristic ZEBRA Diagnostics MZDROP MZGARB - MZGAR1 MZLIFT - MZBOOK MZLINK - MZLINT MZRELB - MZRELL