Data storage and frame control in AMPLE

Kevin Doyle
This article uses material from the AMPLE Nucleus Programmer's Guide published by Hybrid Technology, with their kind permission.
There are several new words available in AMPLE Nucleus to give the advanced AMPLE programmer enhanced data storage facilities and better access to the contents of the number stack.
The new words are DIM and ARRAY for data storage and FVAR, FRAME?, FRAME!, FRAME and FCOPY for stack control.

Data storage words

DIM – reserve memory

sizenumber DIM => address
DIM reserves a specified amount of memory for the program to use for data storage, and leaves the address of the first location. It is usually used with ARRAY to define an array, in which case the number before DIM is the maximum array element number. See ARRAY for more information.
DIM can also be used without ARRAY, leaving you to access memory directly. The DIM is included in a simple definition which serves to give the memory block a name, like a GVAR variable, and lets you include address calculations to make access easier, like ARRAY. This allows you to create custom storage words, for single byte arrays, faster access store, specially-indexed arrays, string variables, operating system interface control blocks and many more. DIM reserves (sizenumber+1)*2 bytes.
Without the checking of ARRAY, it is up to you to make sure not to use memory outside the DIM block.
READY clears all DIM memory.
Related words: ARRAY #! #? #+! #B! #B?
Some examples:
"list" [ 10 DIM ARRAY ]
  % array with elements 0 to 10
"chars" [ 4 DIM #+ ]
  % array with one-byte elements 0-10
  % so,
255 0 chars #B!
  % stores 255 in element 0
A general purpose string store and fetch definition:
"$!" [                 % does string address $!
LEN #212 #B!           % store length
LEN FOR( 1 #+          % next location
1 $- ASC #212 #B!      % store character
)FOR $2                % discard empty
#2                     % string and extra
]                      % number from loop
"$?" [                 % does address $? => string
#11 #B? FOR( 1 #+      % next character
#11 #B! $CHR $+        % fetch character
)FOR #2                % discard extra no.
$REV ]                 % reverse string
Using the words defined above:
"$var" [ 63DIM ]       % define string
                       % variable
$IN $var $!            % get string and
                       % store (for use
                       % inside [])
$var $? $OUT           % fetch string from
                       % store (for use
                       % inside [])
Further information:
The first use of each DIM instruction finds and reserves the memory, and subsequent uses ignore the size number. If the size is less than zero, a value of zero is used. The size (in words) is stored as a two-byte number at the number at the address minus 2. The maximum size theoretically possible is 16383, though in practice there will never be enough memory for this amount!
The structure of the DIM memory block, relative to the address returned by DIM, is as follows:
Information                 Location
––––––––––––––––––––––––    –––––––––––
No. of elements in array    address – 2
element 1                   address
element 2                   address + 2
...
DIM takes memory from the program area. The consumption is shown by MEM. DIM memory is cleared by any command that moves or removes user words. From the keyboard, only the first execution of DIM reserves memory.
Where DIM memory is required only temporarily, for example for a control block, you can use the DIM instruction in-line with no name. For example, a word to open files using OSFIND:
"fopen" [              % filename opno fopen => channelnumber
10 DIM #11             % filename buffer
LEN FOR(               % loop to transfer
1 $- ASC               % characters into #
212 #B!                % filename buffer
1 #+)FOR $2            % incrementing address
13 #12 #B!             % store CR character
#12                    % leaves: address opno
&FFCE CODE             % OS call YX CA => PA
#12 #2 &FF AND         % leave channel number
]
Using the word defined above:
"input" &40 fopen      % => channelnumber
"output" &80 fopen     % => channelnumber
"update" &C0 fopen     % => channelnumber
(see BBC Micro User Guide for details of use of OSFIND)

ARRAY – access array element 

elementnumber ARRAY => address
ARRAY is used in the definition of arrays. It is usually used after DIM, which reserves storage from program memory, in a word definition which serves to give a name to the array.
For example:
"totals" [ 10 DIM ARRAY ]
    |       |
array name  maximum element number
            (11 elements in array)
The array elements are numbered from 0 to the maximum specified, though you will often leave 0 unused. Array elements are stored to (set) by #! and fetched from (read) by #?. The array element number goes immediately before the array name.
The array definition can include instructions that process the element number for special applications, for example two-dimensional arrays.
Related words: DIM #! #? #+! #B! #B?
Some examples:
"values" [ 20 DIM ARRAY ]
0 1 values #!          % store 0 in element 1
3 values #? NOUT       % display element 3
"matrix" [             % 10 x 10 array
1 #- 10 #* #+          % col row matrix
100 DIM ARRAY ]        % => address       
"dispmatrix" [         % display matrix
10 FOR( COUNT
 10 FOR(
  #11 COUNT matrix #? NOUT SP
 )FOR #2 NL
)FOR ]
"plvar" [              % player-local variable
PNUM 8                 % - each player has a
DIM ARRAY]             % private location!
Further information:
ARRAY performs the following:
1) Converts the element number into the location address
2) Checks that the address is in range
ARRAY is sometimes worth using in addition to a simple DIM merely for its range checking function.
Any arrays created by a program remain in existence after it finishes, but they are discarded by those commands that stop any program that is executing, that is, COMPACT, SAVE, LOAD, NEW etc.

Stack control words

FRAME – set frame pointer to top of stack

FRAME marks the current top of the number stack frame which can then be accessed like an array, using FVAR. The top item becomes the first element. The pointer to the previous stack frame can be saved and restored with FRAME? and FRAME!. Music events use a stack frame to hold action variables, and therefore FRAME is undefined over music events, so you should save and restore FRAME if necessary.
For examples see FVAR.
Related words: FVAR FRAME? FRAME!

FRAME! – write frame pointer 

pointernumber FRAME!
FRAME! sets the stack frame pointer to the value supplied. It is used to restore the pointer to the value read with FRAME?.
Related words: FVAR FRAME FRAME?
For examples see FVAR.

FRAME? – read frame pointer 

FRAME? => pointernumber
FRAME? reads the value of the stack frame pointer. It is used to save the pointer value on the stack for later restoration by FRAME!.
Related words: FVAR FRAME FRAME!
For examples see FVAR.

FVAR – access stack frame item 

elementnumber FVAR => address
FVAR is used to access the items in the stack frame marked by FRAME as elements of an array. It is used for convenient access to temporary values on the stack, and to access temporary variable storage on the stack.
It takes the element number and returns the address of it, for use by #?, #!, #+!, #B? and #B!. The position of the top of the stack when FRAME was used is element number 1.
Related words: FRAME FRAME! FRAME?
Some examples:
FRAME 4 FVAR #?        % copy the fourth item
                       % to the top, so,
                       % 4321 => 43214
"top3out" [            % set frame pointer
FRAME                  % and then display top
3 FOR( COUNT           % three items on the
FVAR #?                % stack leaving them
NOUT SP )FOR ]         % unchanged
"#pick" [              % does n #pick => number
                       % copy nth number down to the top
FRAME?                 % save frame pointer
FRAME                  % set frame pointer
#12 2 #+               % get n, adjust it
FVAR #?                % read value and
#12 FRAME!             % restore frame
]                      % pointer

FCOPY – copy n items below stack frame to top of stack

FCOPY copies a group of adjacent items in the stack frame marked by FRAME to the top of the stack. It is used for duplication of groups of values within the stack (cf. #11, #212 and #2121 which copy 1 or 2 items from the top of the stack) and for access to groups of items on the stack.
For example:
FRAME 3 FCOPY          % copy the 3 items below
                       % FRAME to the top, so,
                       % 4321 => 4321321
Related words: FVAR FRAME FRAME!

Published in AMPLINEX 001, September 1987