Back To Basics - Part 12

In the final part of the series, David Matthewman looks at direct memory access from Basic

Last month I looked at accessing files, and saw that numbers can be stored in files in a format which, while compact, is not easy to read. Integers are stored in four bytes and real numbers are stored in five, as a bit-pattern giving a binary representation of the number rather than as a sequence of characters.

This is also the case with variables stored in memory. When a program contains the line:

var% = 1048576

the Basic interpreter stores the number 1048576 in memory at an address which it remembers.

The variable var% is said to have this address, because that is where its value, 1048576, is stored.

You are unlikely to need to know this address. Indeed in Basic there is no easy way of finding it out, as there is in languages such as C.

However, sometimes it is useful to store data directly to memory and to have some control over where related items of data are stored.

There can be considerable speed advantages to this approach when dealing with long lists of data.

Indirection operators

In Basic, memory can be accessed directly by using indirection operators. There are four of these: ? ! | and $. Preceding a variable with one of these will cause the value of the variable to be treated as an address.

The variable therefore must be an integer, and one with a value within the bounds of memory. In practice it is not advisable to try to set this up yourself unless you are really sure what you are doing. Basic provides a way of doing this automatically, by using the DIM statement.

Previously we have used the DIM statement to set up an array. However, if used in the form:

DIM addr% bytes%

with the second number not enclosed in brackets, the statement reserves a block of memory bytes% long, and sets the value of addr% to the address of the first byte.

It is important not to confuse this with the address of addr%, which is also an address but not one of interest.

It is the value of addr% which is the address of the block of memory and which we can use with indirection operators.

?addr% = byte%

stores a byte - with a value between 0 and 255 - at the address given in addr%.

!addr% = integer%

stores a four-byte word - an integer variable - at the address given in addr%, and the three subsequent bytes.

|addr% = real

stores five bytes - a real variable - at the address given in addr%, and the four subsequent bytes.

$addr% = string$

stores a string at the address in addr% and however many bytes are needed afterwards. A carriage return - ASCII 13 - is stored on the end to terminate the string.

Memory offsets

Unlike the PTR# variable in files, addr% is not updated each time memory is written to and from.

The program therefore needs to keep track of how far through the block of memory it has progressed. Constructions such as:

?(addr% + offset%)

are legal within Basic fortunately, although the brackets are necessary. Without the brackets, offset% would be added to the contents of the memory at addr%, rather than to the value of the variable addr% itself.

The program:

DIM addr% 100
?addr% = 32
?(addr% + 1) = 64
!(addr% + 2) = 1048576
|(addr% + 6) = 6.02E23
$(addr% + 11) = "Hello there!"

will store a sequence of bytes in memory from addr% to addr% + 23. The sequence, incidentally, will be 32, 64, 0, 0, 16, 0, 133, 248, 244, 126, 207, 72, 101, 108, 108, 111, 32, 116, 104, 101, 114, 101, 33, 13, but this is of little interest.

Lines three and four of the program above could also be written:

addr%?1 = 64
addr%!2 = 1048576

which is a compact shorthand way of using offsets from a base address. It can only be used with ? and ! however, not | and $.

Reading data

The same operators can be used to read data from memory. It is important to realise that - for example - the ! operator will read any sequence of four bytes, and not just one that was written with ! in the first place. The block written in the last program could be read as follows:

byte1% = ?addr%
byte2% = addr%?1
word% = addr%!2
real = |(addr% + 6)
greetings$ = $(addr% + 11)

and also into an array of four-byte words by:

FOR i% = 0 TO 24 STEP 3

Listing 1 loads a file into memory byte by byte. It then 'scrambles' the file by EORing it with an integer, and saves it back to the disc.

Listing 1

REM text file scrambler
key% = 1143697892
file% = OPENIN("<BackToBasics$Dir>.Text")
IF file%=0 PRINT "File cannot be found":QUIT
size% = EXT#file%
DIM memory% size%
FOR i% = 0 TO size%
  memory%?i% = BGET#file%
FOR j% = 0 TO size% STEP 4
  memory%!j% = memory%!j% EOR key%
file% = OPENUP".Text")
FOR k% = 0 TO size%
  BPUT#file%, memory%?k%

As with last month, copy the contents of the BasicProg directory on the cover disc to another disc and run the SetVar obey file before running the listing.

Although the file is loaded and saved byte by byte, using BGET#, BPUT# and ?, it is 'scrambled' in blocks of four bytes using the ! operator for efficiency.

Using EOR to perform the 'scrambling' has one signficant consequence - the original text can be recovered by simply running the program a second time.

A possible improvement to the program would be to make the key% scrambling code user-defined, which would provide a very primitive form of password protection.

Click here to download the example files

TOFLA Librarian's note:
The second half of this article consisted of a summary of what literature was available for further BASIC development. Most of this information is now out of date, and so I've chosen to remove it.

Source: Acorn User 144 - July 1994
Publication: Acorn User
Contributor: David Matthewman