!TITLE Store Mapping !KEY A variable declared in an IMP80 program is allocated storage on the stack (Section 3). It is possible in IMP80 to give an alternative name to such a variable. It is also possible to refer to an arbitrary store location, not necessarily within the stack area, and operate on it as though it held a variable of a specified type. "Store mapping" is the name given to this technique. It is useful for the following reasons: * to enable entities not declared within a program to be accessed and operated on by the program, without recourse to machine code or other machine specific features * to save space in main store (see first example below) * to access a variable both as declared, and as a set of sub-variables; for example, a variable of type %integer can be accessed as several separate variables of type %byte %integer !PAGE * to access an array element as a simple variable, with consequent saving of machine time * to improve the clarity of a program The store mapping facilities in IMP80 are provided by means of several components: * reference variables * user-written mapping functions * the ADDR function * the standard mapping functions != j are stored. By keeping only these values in a one-dimensional array A - which is global to the map - we can make economical use of store without losing the symmetrical appearance of the array X. The array is first assigned values and then various references to it involving %integer ALPHA and %integer %name BRAVO are made. !PAGE %begin %integer %array A(1:210) %integer ALPHA, I, J %integer %name BRAVO %integer %map X(%integer I,J) %signal %event 6 %unless 1<=I<=20 %and 1<=J<=20; ! Array bound check. %result == A(I*(I-1)/2 + J) %if I>J %result == A(J*(J-1)/2 + I) %end %for I = 1,1,20 %cycle X(I,J) = I\\2 + 2*I*J + J\\2 %for J = 1,1,I %repeat !PAGE ALPHA = X(17,10) {ALPHA assigned the value of the variable returned {by the call X(17,10) of map X. BRAVO == ALPHA {BRAVO made to refer to ALPHA (thus BRAVO is now {synonymous with ALPHA). BRAVO = 4 {BRAVO (i.e. ALPHA) assigned the value 4. BRAVO == X(16,9) {BRAVO made to refer to the variable returned by {the call X(16,9) of map X. BRAVO = 4 {BRAVO (i.e. the variable it currently refers to) {assigned the value 4. ALPHA = BRAVO {ALPHA assigned the value of the variable to which {BRAVO currently refers. This value is 4. X(6,15) = 17 {The variable to which reference is returned by {the call X(6,15) of map X is assigned the {value 17. ALPHA = X(15,6) {ALPHA set to the value of the variable returned {by the call X(15,6) of the map X. From the {previous statement, and bearing in mind the {symmetry of X, this value is 17. : %end %of %program !PAGE 2) Reference variable used as a procedure parameter When a procedure is called, its formal parameters - in the example below SIZE and STATUS - are assigned values according to their types and to the actual parameters used in the call. SIZE is of type %integer and so has the current value of TOTAL assigned to it; STATUS is of type %integer %name and so is "pointed at" the integer variable RETURN. This example is included because procedure %name-type parameters provide a method for renaming variables, and as such represent a common application of store mapping techniques. %routine %spec TEST(%integer SIZE, %integer %name STATUS) %integer TOTAL, RETURN : TOTAL = 3 TEST(TOTAL, RETURN) ! TEST called: formal parameters assigned values. : !PAGE 3) Mapping a direct access file The following map enables the programmer to treat a direct access file held on backing store as though it were a real array with declaration %real %array FILE(1:NBLOCK, 0:255), where NBLOCK is the number of blocks in the file. In use, the only difference from a normal array is that a closing call = FILE(0,n) must be made. In this implementation of IMP80, real variables are 32-bit entities, and each block of a direct access file consists of 1024 8-bit bytes. It is assumed that the direct access file has been associated with channel number 1 prior to the execution of this program. Note that when a virtual memory operating system is being used, a simpler and better method for mapping arrays onto files may be available - see later example. !PAGE %real %map FILE(%integer BLOCK, ELEMENT) ! The external routines specified below relate to the use of direct- ! access (DA) files. They are the standard routines provided in most ! implementations of IMP80, but do not form part of the language ! definition. Further details for some implementations are given in ! Appendix B. %externalroutinespec OPEN DA(%integer CHANNEL) %externalroutinespec CLOSE DA(%integer CHANNEL) %externalroutinespec READ DA(%integer CHANNEL, %integername BLOCK, %name START, FINISH) %externalroutinespec WRITE DA(%integer CHANNEL, %integername BLOCK, %name START, FINISH) %constant %integer NO=0, YES=1 %own %integer CURRENT BLOCK = 0, BLOCK CHANGED = NO %own %integer LAST ELEMENT %own %real LAST VALUE %own %real %array BUF(0:255) = 0(256) !PAGE %unless BLOCK>0 %and 0<=ELEMENT<=255 %start ! Could be an error, or the closing call. ! In either case, tidy up and close the file. WRITE DA(1, CURRENT BLOCK, BUF(0), BUF(255)) %if BLOCK CHANGED=YES CURRENT BLOCK = 0 %if BLOCK=0 %start; ! Closing call. CLOSE DA(1) %result == BUF(0); ! Irrelevant in this case. %finish ! Error - parameters out of range. %signal %event 6 %finish %if CURRENT BLOCK=0 %start; ! First call. OPEN DA(1) CURRENT BLOCK = BLOCK READ DA(1, CURRENT BLOCK, BUF(0), BUF(255)) %finish %else %start; ! Not the first call. !PAGE ! Has the value of the last element returned by the map been ! changed? If so, note the fact so that the block is written back ! when no longer required by the map. BLOCK CHANGED = YES %if BUF(LAST ELEMENT) # LAST VALUE %if CURRENT BLOCK # BLOCK %start ! Block required differs from that currently held in BUF. ! Write currently held block back if it has been changed. WRITE DA(1,CURRENT BLOCK,BUF(0),BUF(255)) %if BLOCK CHANGED=YES ! Now get block required. CURRENT BLOCK = BLOCK READ DA(1, CURRENT BLOCK, BUF(0), BUF(255)) BLOCK CHANGED = NO; ! Reset change flag. %finish %finish LAST ELEMENT = ELEMENT LAST VALUE = BUF(ELEMENT) %result == BUF(ELEMENT) %end; ! Of %real %map FILE. !> !) %array %format () !PAGE Example: This example is of a record array format being used in an IMP80 program running under the operating system EMAS. The integer function SMADDR returns the address of a file connected in the virtual memory of the user process. %routine PAYCHECK(%integer CHANNEL, RECNO) %integer I, J, K, START, LENGTH %string(11) NAME %external %integer %fn %spec SMADDR(%integer CHANNEL, %integername L) %record %format PAYF (%string(11) SURNAME, %integer AGE, SEX, YEAR, %integerarray SALARY (1:12)) ! Each record thus formatted contains 72 bytes. %record(PAYF)%array %format PAYAF(1:RECNO) %record(PAYF)%array %name PAY : ! Assume that a file was associated with channel CHANNEL, ! prior to the execution of the routine. !PAGE START = SMADDR(CHANNEL,LENGTH); ! File now connected. %if LENGTH < 72*RECNO %start PRINTSTRING("File too small:") WRITE(LENGTH,1); NEWLINE PRINTSTRING("Must be at least") WRITE(72*RECNO,1); NEWLINE %return %finish PAY == ARRAY(START,PAYAF) ! Now record array name PAY has been mapped onto the file. ! Note that START was set by the SMADDR call. : NAME=PAY(I)_SURNAME %if PAY(I)_SALARY(J)>350 .... : PAY(K)_YEAR=1978 : %end; ! Of %routine PAYCHECK. } !> !