Block PEEK And POKE For Atari

Ronald P Lambert

Here is a convenient way to eliminate long initialization delays
caused by POKEing large amounts of data into memory. It works entirely
in BASIC and works very fast. The demonstration program moves the entire
character set in an instant and redefines the keyboard as a Dvorak
layout. This technique can be used on all Atari 400/800, XL, and XE
computers and is recommended for intermediate to advanced BASIC
programmers.

PEEK and POKE are among the fastest commands in BASIC. But because
they handle only one byte at a time, it can take a while to transfer
large blocks of data from one area of memory to another. We've all
waited while programs with long loops PEEK a series of memory locations,
or READ numbers from DATA statements, and then POKE the numbers into
memory somewhere else. Perhaps the program is redefining the character
set, or setting up player/missile graphics, or building a machine
language subroutine, or creating a new keyboard definition table.
Whatever's happening, it slows things down.

Lengthy FOR-NEXT loops with PEEK and POKE or READ and POKE are the
primary cause for tedious delays while these programs initialize. No one
likes to sit staring at a blank screen for very long. The program
usually prints a message like "Please wait while I initialize," but
isn't there a better way? Sometimes a machine language subroutine can
help speed things up, but if you can't write in ML yourself, finding a
routine exactly suited to the needs of your program can be difficult.

Fortunately, Atari BASIC's flexibility provides a solution. It's
possible to transfer large blocks of data from Read Only Memory (ROM) or
program lines to any area of Random Access Memory (RAM) virtually
instantaneously--with BASIC commands only. The secret is called the
string offset technique. By modifying the variable value table (a
section of memory which keeps track of BASIC program variables), you can
redefine any string and relocate it anywhere in memory.

Here's a quick overview of how the technique works. Suppose you set
up a string called ROM$ which contains a block of data found in ROM--the
character set data, for instance. Next, you set up another string called
RAM$ in the area of RAM to which you want to move the data contained in
ROM$. To copy the data from ROM to RAM, then, all that's required is
the simple statement RAM$ = ROM$. Is that easy enough? Using the string
offset technique, any portion of ROM--or all of ROM, if you make the
strings big enough--can be copied into RAM in the blink of an eye.

The Variable Value Table

To use the string offset technique, you have to learn how to modify
the variable value table and the string offset pointers. This isn't too
difficult if you tackle the job one step at a time.

The first step is to make things easier for ourselves by insuring
that ROM$ and RAM$ are the first variables found in the table. We can do
this by making ROM$ and RAM$ the very first variables defined in the
program. Enter NEW as a direct command before typing the first program
line containing these names. Then dimension the variables in this order:

10 DIM ROM$(length),RAM$(length)

where length is the length of each string in characters as
required by your program. If you're moving character set data, for
instance, you'd dimension these strings to the number of bytes in the
character set--1024 bytes in graphics mode 0 or 512 bytes in modes 1 and
2. (Usually you'll dimension ROM$ and RAM$ to the same length if you're
transferring a block of memory.)

However, if you are using an Atari 400 or 800 with the old BASIC
revision A, a major caveat applies. You cannot move blocks of
memory that are exact multiples of 256 bytes. Attempting to move
blocks of this size will trigger the infamous BASIC lockup bug, freezing
your computer until you turn the power off and back on--which will, of
course, result in the loss of your program. [For more information on
the lockup bug, see this month's "Readers' Feedback" column. -Ed]
You can determine your version of BASIC by entering PRINT PEEK (43234).
If the value returned is 162, you have revision A. If 96 is returned,
you have revision B (built into most 600XL and 800XL models), and 234
indicates revision C, available on cartridge from Atari and built into
the XE models.

The string offset technique will work as described with revision A as
long as you make sure your block length is not an exact multiple of 256.
So for this example you should substitute 1025 instead of 1024. This
will transfer an extra byte of memory following the character set, but
that doesn't cause any problems and it prevents the lockup bug from
biting.

The second step is to make BASIC think that ROM$ is actually 1024
bytes long (remember, use 1025 for revision A BASIC). The DIM statement
reserves memory for the string but doesn't actually define the string.
Use a line like this:

20 ROM$(length) =" "

By defining the last character in the string as a space, BASIC is
forced to treat ROM$ as a 1024-character-long string, even though no
other characters have been defined.

The third step is to calculate the location of the variable value
table in memory, with a statement like this:

30 VT=PEEK(134)+PEEK(135)*256

The variable VT equals the starting location of the variable value
table. Each string which is declared in an Atari BASIC program has
eight bytes in this table. We'll see the significance of these bytes in
a moment.

After these variables are set up, the first eight bytes in the
variable value table (VT to VT + 7) contain information for ROM$, and
the next eight bytes (VT+8 to VT+ 15) contain information for RAM$.

Locating The Strings

To use the string offset technique, we're primarily interested in the
third and fourth bytes for each of these two variables in the variable
value table. The memory locations for these bytes can be expressed as
VT+2 and VT+3 for ROM$, and VT+10 and VT+11 for RAM$.

What do these bytes signify? Briefly, each pair of bytes is a
low-byte/high-byte combination that indicates the relative
displacement of each string from the starting location of the first
string in the program. Since we've made sure that the first string in
the program is ROM$, the values stored in VT + 2 and VT+3 for ROM$ will
both be zero. And since we've also made sure that RAM$ is the second
string in the program, the values stored in VT+10 and VT+11 for RAM$
depend on the length of ROM$

For instance, if ROM$ is dimensioned to 1024, then the memory which
BASIC sets aside for RAM$ must begin 1024 bytes after the start of ROM$
to leave room for ROM$. Therefore, the value stored in VT+ 10 is zero,
and the value stored in VT+ 11 is four. (Since VT + 11 is the high byte
of the offset, it's multiplied by 256, which equals 1024.)

Actually, the memory for ROM$ and RAM$ does not begin at these
locations. Instead, you have to add another value indicated by the
string offset pointers at memory locations 140 and 141. If you
use this statement:

40 SF =PEEK(140)+ PEEK(141)*256

then the variable SF returns the number that should be added to the
relative displacement values given in the variable value table. (Since
the relative displacement of the first string is zero, this means that
SF always equals the address of the first string.)

The reason for this seemingly complicated arrangement, incidentally,
is that the computer can now easily relocate strings as the program
length changes simply by altering the offset pointers.

Setting The Table

Now it's clear how the string offset technique works: We can relocate
a string anywhere in memory by merely POKEing different values into its
relative displacement indicators in the variable value table.

For example, suppose we want to move ROM$ to the starting memory
address of the standard character set in ROM, which is location 57344.
We subtract the amount of the string offset (SF) from 57344, and convert
the remainder into low-byte/high-byte numbers. Then all we have to do
is POKE LS into VT+2 and POKE HS into VT+3, and ROM$ is moved to the
proper location. The statements might look like this:

The usual place to set up a new character set is below RAMTOP-- the
memory location returned by PEEK(106)*256. Some people prefer to move
RAMTOP down by POKEing a lower number into register 106, issue a new
GRAPHICS command to set up a new display list and screen memory below
the altered RAMTOP, and then put the new character set above the new
RAMTOP. There are advantages and disadvantages to each method, including
a "RAMTOP dragon" to watch out for. We'll stick to the easiest method
for this example. Let's simply put the new character set eight pages
(2048 bytes) below RAMTOP. This leaves enough room for the 1024-byte
character set, plus another 1024 bytes for the display list and screen
memory in graphics mode 0.

We move RAM$ to this location by figuring the proper values and
POKEing them into VT+10 and VT+11:

Instantly, the character set in ROM is copied into RAM, where it can
be customized to suit our purposes.

Two Potential Problems

There are two things to watch out for when using the string offset
technique. First, if you set up a string in a section of RAM where vital
tables or pointers are stored, then do anything to change the contents
of the string, or press BREAK and enter a direct command (which causes
BASIC to shift strings and all their contents in memory), the computer
may behave very strangely. You'll probably have to turn the machine off
and on again to regain control.

Second, you cannot POKE a negative number into the variable value
table without getting an error message. How, then, can you move a string
to a location in memory lower than the value (SF) indicated by the
offset pointers? Simple. POKE the offset pointers to zero, and POKE
memory locations 140 and 141 to zero. (Make sure you do this before
relocating any strings, or they'll all be moved again when you change
the offset pointers.) But don't leave zeros in locations 140 and 141.
Save the original values and POKE them back in when you're finished
transferring the data.

A Dvorak Demo

The program following this article demonstrates the string offset
technique and accomplishes several things. First, it copies the standard
character set from ROM into RAM (eight pages below RAMTOP) and modifies
it so that the CTRL key characters can be recognized more easily. If you
press CTRL-A, for example, you won't get the usual graphics symbol;
you'll get an underlined A, so you can see at a glance which keys to
press to type that character. This way, you can enter ATASCII (Atari
ASCII) characters directly into memory from statements in program lines
without using DATA statements and slow, one-byte-at-a-time POKEs,
because all the characters are immediately recognizable. This character
set modification is accomplished in an eye-blink.

Second, the program copies the keyboard definition table from ROM and
loads it into memory page 6, a normally unused portion of RAM from
locations 1536-1791. Then the program modifies the keyboard table to
create a Dvorak keyboard layout. Designed by August Dvorak after 20
years of scientific study and testing, the Dvorak keyboard makes things
as easy as possible for typists, in contrast to the conventional QWERTY
keyboard, which doesn't put the most frequently used keys on the home
row. Many typists are able to convert from QWERTY to Dvorak touch-typing
within a few days, and often find they can type faster with
substantially fewer errors.

The Dvorak keyboard portion of the program will not work with the
older 400 and 800 models because it relies on the KEYDEF pointer at
locations 121-122. This pointer was added to the improved operating
system in the XL and XE models, and is not implemented in the original
Atari operating system ROMs. Owners of 400s and 800s can still use the
redefined character set portion of the example by simply omitting all
lines numbered higher than 215. If you are using revision A BASIC,
you'll also need to change the 1024s in lines 10 and 20 to 1025s.

Notice that this program must deal with the problem mentioned above:
The memory address 1536 is lower than the value for SF, so the string
offset pointers at locations 140 and 141 have to be changed.

A FOR-NEXT loop is used to enter ATASCII characters 0 through 26, so
this part of the program takes a little longer--almost a whole second.
You could make it run even faster by typing the CTRL key characters
directly in string assignment statements, as seen in lines 140 to 170.
This is where the new character set could come in handy.

As a final bonus, the program demonstrates a customized keyboard
entry routine that works faster than the GET function. It does this by
reading a hardware location (53769), then using the keyboard conversion
table located in ROM (64337 to 64592) to translate the keyboard codes
into ATASCII codes the same way the operating system does.

When the program runs, it lets you toggle back and forth from QWERTY
to Dvorak, just like on an Apple IIc. Press SHIFT-ESC to toggle. If you
become a real Dvorak fan, you can even find keycap stickers at many
office supply stores to modify your keyboard. The accompanying figure
shows the Dvorak layout.

Additional Notes

A few modifications to the standard Dvorak layout were necessary
because of the special functions and extra keys on the Atari keyboard.
The seldom-used brackets may be typed with CTRL-9 or CTRL-O. The + =
key, normally located at the upper right of the Dvorak keyboard, has
been moved down. The * \ key has been retained in its standard Atari
position because these characters have extra use as arithmetic functions
in programming. Since the Atari has no cent symbol, this has been
replaced with the vertical line as uppercase 6. In place of the asterisk
(uppercase 8 on the Dvorak keyboard) is the backslash. The ` " key has
been exchanged with the ; : key to avoid conflict between the CTRL-up
arrow and CTRL-semicolon.

The Atari logo key is the inverse video key on the Atari 400/ 800,
and it is reversed with the right SHIFT key on those models.
Regrettably, the Atari has no dash in its character set. While the
useless underline could be redefined as a dash for screen display, most
Atari printers also lack the dash.

If you enter NEW or load a new program after this one is run, the new
character set with readable CTRL key characters remains active (as long
as the new character set is not overwritten). Press SYSTEM RESET or POKE
756,224 to restore the old character set. The following POKEs switch on
the Dvorak keyboard even after a NEW command: POKE 121,0:POKE 122,6. To
switch back to QWERTY, use POKE 121,81:POKE 122,251.

The next time you need to transfer large blocks of data from one
portion of memory to another, try using the string offset technique. It
gives you the best of both worlds: the convenience of BASIC and
near-machine language speed. Never again will you have to sit staring
at a blank screen waiting for your programs to move large amounts of
data in memory.