by Stephen Maguire and Evan Rosen
Let's quickly finish up discussion of the code for last issue's one-player "Dot-Maze" game and then move on to new topics.
On screen 16 last month was the word GET-STICK which stored the horizontal increment read from joystick 0 in the variable DX, and the vertical increment into DY. (Remember, the increments may be -1, 0, or 1.) Built into GET-STICK, however, is a delay, determined by the variable STICK-DELAY, to slow the Wumpus down when traveling over open spaces in the maze. An open-space move is determined by the logic on line 4, which looks for a stick move in one and only one of the four directions up, down, left, or right, and line 5 which checks to see that the variable WUMPOS is over an inverse-video blank, value 128. The Wumpus is not slowed when running along the diagonal roads.
The word MOVE-OK? on screen 15 picks up the values in the variables DX and DY which are set by GET-STICK and checks to see if they are legal. The 198 and 199 in the definition are inverse video screen codes for 70 and 71. That is, 70 + 128 = 198 and 71 + 128 = 199. If the move is valid, then MOVE-OK? leaves a true flag, which is a 1. Otherwise MOVE-OK? leaves a false flag, a 0.
On screen 17 was PICK-ONE, a handy, random-picking routine which is aided by hardware-supplied random bytes from location 53770, which always provides a fresh random byte. Given a positive number n, PICK-ONE will randomly pick a number from 0 to n-1 and leave it on the stack. PICK-ONE is used in INITIALIZE, also on screen 17, to pick an initial position for the WUMPOS. INITIALIZE also sets the variables STICK-COUNT (the stick delay counter) and POINTS (the score counter) to 0 to clear any data left from a previous game.
The words SHOW-SCORES and FINISHED? on screen 18 are fairly self-explanatory. Remember, if you don't know what something does, like 125 EMIT, for example, you can always try it out.
Lastly, the word DOT-MAZE on last issue's screen 19 puts all the previous words together to run the game. Note the check on the value in the key input buffer, location 764, on line 10 of screen 19. This allows ending a game early by pressing any key. If the player wishes to start a new game, pressing the "Y" key will let him exit the present game and then go to a new one as the "Y" is picked up by the word FINISHED?. More on this key buffer in a moment.
This issue's "Snow" game is somewhat representative of FORTH-style coding in general. At the end of the listing you'll find the top word, SNOW, which calls some of the words below it, which in turn call words below them. The words are short, each composed of only about 30 or 40 other words at most, and all words have well-defined tasks which are indicated by the words' names. This is roughly how a program should look. But how do you get it to look this way?
If the programmer already has a precise idea of what a given program has to do, then it is possible to program "top down." That is, the programmer writes out the top word first. The top word will contain many undefined words, and these are defined next. These words will usually still contain undefined words, and the defining process continues down to the level until all words are defined in terms of the original set of words in the FORTH kernel. At this point the program is written and is ready for testing. This "top down" coding style is used with success in much commercial programming and in many languages, FORTH included.
In hobby hacking, however, "bottom up" or "inside out" coding also occurs. For instance, you may program random-number pickers or joystick-reading routines first, just because they happen to be more fun in themselves, or because you don't really know where you're going with the program yet. But eventually, when you have a solid plan in mind for your game (or whatever), make a final pass or two in "top down" style to clean things up. This will greatly simplify debugging, improve clarity and organization and will facilitate the making of changes later on.
For our main topic this issue we would like to define some words which simplify picking up keyboard input from within a running program. Restricting ourselves to single key inputs (rather than sequences or "string" inputs), we can still distinguish at least two different types of keyboard input: "Waiting for a key," and "Looking for a key."
Waiting for a Key, or, "What is your choice, Sire?"
This type of input may be implemented simply using the word KEY. KEY enters a loop and waits for any key to be pressed. When one is, KEY leaves the pressed key's ASCII code (or its Atari-modified ASCII code, called ATASCII) on the stack. Very often, a yes-no answer is required from the keyboard and the program prints a question and indicates it is waiting for a "Y" or "N" to be pressed: This happens so often, in fact, that it makes sense to define a word called Y/N, which can be tacked on after the question string in the word that asks the question.
Y/N may be implemented in many ways. On screens 70 and 71 in this article are two definitions which also illustrate differences between "hobby" or "quick" programming, and more "polished" or "friendly" code. Type in and load the code and type EXAMPLE, which uses the friendly and sophisticated version of Y/N, and note that it will accept only "Y," "y," "N," and "n" and won't let go until it sees one of these.
KEY may also be used to wait for a key that represents a choice from a menu displayed on the screen. That is, suppose we had four options at some point in a game. The code might look like that on screens 72-75. If you edit this into your system and type 72 LOAD (after loading the previous example) and then GAME, you'll get a sample menu.
Of course, this is a simple example, but the words RUN, FIGHT, SPELL and WAIT could have much more complex actions. They might look at some variables which would have information on "who" the enemy was and what "powers" it had what kind of "power" the player had and how his "luck" was holding out, and so on.
So much for "wait for a key" input. But what if you are programming an action game where you don't want to stop everything while the player ponders a move? This requires getting key input of a different sort.
Looking for a Key
Suppose we want to pick up keys "on the fly" rather than waiting for them. We can't use KEY, which enters its own wait-loop. As it happens, in Atari 400's and 800's, location 764 holds a value, called a "key code," which corresponds to the last key pressed and is not absorbed by the operating system. These key codes don't seem to have any rhyme or reason to their numbering scheme, but still they can be used to identify key presses. Let's define a word to tell us a key's key code when we press the key.
: KEYCODE (--)
BEGIN 53279 C@ 7 = 0 =
UNTIL 764 C@ .255 764 C! ;
To find the key code of a key, type KEYCODE (Return), then press the key for which you want the keycode, and then press any one of the yellow console buttons, i.e., START, SELECT, or OPTION. (Location 53279 reads the console buttons, but in a slightly involved way.)
To illustrate one use of picking up keycodes on the fly, we'll make up a simple game called "Snow." The code is on screens 76 through 81, and may be loaded independently of the earlier code. For QS FORTH you must also first load its I/O package to get the word GR. into the system. For APX FORTH, first load the assembler and then the graphics package, to get GR. in. ValFORTH will load the code without extras. To run the game, type SNOW. The idea is to maneuver the bucket, a "U," under the falling asterisk snowflakes without letting any flakes hit the ground. The bucket is moved by pressing the "F" and "J" keys, which is really the point of the illustration. If you're using APX FORTH, then on successive runs of snow you'll need to say XGR SNOW instead of just SNOW.
Space does not permit a long discussion of the code, but here are some highlights. SNOW starts with an initialization routine and then enters a loop which moves the snowflakes and the bucket, and then delays for a while to slow the process down to human speed. The loop doesn't exit until the variable ?DONE becomes non-zero. This can only happen when a snowflake reaches the ground, which is determined in FALL?. Note the use of the random byte register, 53770, in FALL?, which is used to move a flake only one time in eight that FALL? is called.
And that's about it for now, except for a quick word from Dr. Quatro. Doctor?
Yes, yes, boys. The word is:
: BASE36 ( -- )
This little treasure lets us see short strings as numbers. For supposing, we can say now:
BASE36 QUA TRO
and we will get back "ok" because in base 36 "QUA" and "TRO" are numbers. You see it? And then we can also say:
TWA 467 LAX
which means Trans World Airliners flight 467, Los Angeles International airport. Yes, it does!
Evan Rosen and Steve Maguire are the creators of Valforth by Valpar Int.