Does this story sound familiar? One day, Fred found an excellent short program in a magazine article and he decided to translate it for his ZX80. But after entering only a few lines of the program, he ran into a statement like this:
250 DIM N$(4)
“What in the world does that mean?” he asked himself. When he could not answer that, he consulted a friend with a TRS-80. His friend explained that it works just like a regular DIM statement but with strings instead of numeric variables. That is, DIM N$(4) sets up four strings with the same name, N$, but individually numbered 1-4. Fred frantically flipped through the pages of the ZX80 manual and found the terse explanation of the DIM statement on page 89. But there was nothing there about strings. So, he set aside the magazine article and decided to stick with programs written just for the ZX80.
Well, if Fred sounds like some people you know, then consider this article dedicated to them. SYNC is a fine magazine, but it cannot publish every program that will work in the ZX80. And just because you cannot use a statement like DIM N$(4) does not mean that you cannot translate a program that uses it.
Now you 8K ROMers realize that your supercharged machines will DIMension strings without any hocus-POKEus, and you may be tempted to turn the page on me. But stick with me because I think that you will find this technique very interesting if not useful. Anyway, from now on we will be talking strictly in terms of the 4K ROM.
It is true that the people who designed the 4K ROM left out the ability to DIMension strings. However, with a few well-placed POKEs, you can plug up that hole in the 4K ROM.
For example, enter this short program.
100 FOR I=1 TO 4
110 POKE 16450,I+37
120 INPUT A$
130 NEXT I
The A in line 120 is stored in the memory location 16450. The code for an A is 38, So, at present, a 38 is stored at the location 16450. When you RUN this program, it will execute lines 110 and 120 four times. The first time through, line 110 will POKE a 38 (1+37) into the location 16450. Then, line 120 will INPUT A$. But the second time through, line 110 will POKE a 39 (2+37) into location 16450. Since 39 is the code for a B, line 120 will then INPUT B$. This program actually changed itself! The third time through the loop, line 110 POKEs a 40 (3+37) into the memory location, and line 120 will INPUT C$. When the program is finished, it will have stored four strings: A$, B$, C$, and D$. You can imagine that these are numbered 1-4 because when I=1 you INPUT A$ and when I=4 you INPUT D$.
RUN the program and enter four words. Now change line 120 to 120 PRINT A$ and then GO TO 100. The method will work with INPUT, PRINT or any other string functions.
Some of you may thinking, “Big deal. What good is all this?” I think that the following two programs will illustrate the virtues of this technique.
The first program is a simple sort program. We all know that computers are very good at putting numbers in order. And since computers store letters as numbers, they are also good at putting words in order (alphabetical order, that is). The program will alphabetize up to 25 words and will display up to 22 of them.
Using the program is simple. Just enter the number of words you want to alphabetize and then enter the word with a NEWLINE after each one. When you have had the last word, the program takes over, and, a few seconds later, the words are displayed in alphabetical order.
I think that it is worth noting line 240. I do not know if other Basics let you use inequalities with strings, but ZX80 Basic does. That is a very nice feature. If you want to put the words in reverse order, simply reverse the inequality.
This program is not so great by itself. But it could be developed into a good utility program for handling a list of the names of friends for an address book or names of students for a grade book. It could possibly be adapted to help teach dictionary skills.
The second program illustrates the technique by computerizing a card game played something like Rack-O (by Milton Bradley). The POKEing is used to call each player by his name rather than the impersonal PLAYER 1, PLAYER 2, etc.
In this game, the players are dealt ten numbered cards. The remaining cards are placed face down, and the top card is turned face up to form a discard pile as in Gin. The object is to get ten cards in numerical order (not necessarily consecutive order). This is done by drawing a card from either pile and exchanging it for one of your cards.
In this version, of course, the computer handles all the cards. First, it shuffles them and places them into each player’s “rack.” Then the first player’s cards are displayed and he is asked if he wants the card showing in the discard pile. If he does not, he enters “NO,” and he is given a card from the face down pile. If he does not want that card, he enters “NO” again and his turn is over. If he decides to take either card, he enters “YES” and the computer will ask where in his “rack” he wants the card to go. The player then enters a number 0-9. His card will be placed in that position, and his turn will end.
At the end of each player’s turn, the computer will display the cards that the player has in order so far. Then a NEWLINE will start the next player’s turn.
The game ends when one player get his cards in order. To start a new game you have to RUN the program again.
If you have more than 1K of RAM, you could probably teach the computer how to play the game and then play against it. You could also allow more than four players. But be careful. Any modifications of these programs may affect the POKE locations. To make sure, LET I=0 and then GO TO the POKE statement in question. If the next statement does not contain a 9$ after you do that, you will have to change the POKE location until it does. But trial and error will not work very well if you make big changes or if you write your own programs. Then you will need a more exact method of finding the location of a specific byte in a program.
One way is to count the bytes in each line and add them up. Every line has two bytes for the line number (no matter how many digits), one byte for the Newline at the end of the line, and one byte for every keystoke in between. So, in the example program, line 100 takes 9 bytes, line 110 takes 14 bytes, and line 120 takes 3 bytes up to the A for a total of 26 bytes. Add 26 to 16424, which is the starting address of every 4K ROM program, and you get 16450. That is the actual location of the A in memory. However, counting all the bytes in a long program is a very tedious chore. I found a better way.
When USR(24) is put into a program, it stops the execution of the program and returns the starting address of the next line. To illustrate, insert 115 PRINT USR(24) into the short program and then GO TO 115. 16458 appears on the screen because that is now the starting location of line 120. Line 115 takes 11 bytes so, when it is deleted, line 120 will move up 11 bytes to 16447. Adding the three bytes of line 120 again shows that the location of the A is 16450. USR(24) is an invaluable utility whenever you need to know the location of a byte in your program.
A lot of programs out there in books and magazines can be adapted to the ZX80. But sometimes you really have to work to get them to. Now the lack of DIMensioned strings is no longer a problem. There are other shortcomings in the 4K ROM in dealing with other Basics. Yet, with a little determination and ingenuity, you can usually get around them.
Program 1. Alphabetize (4K ROM; 1K RAM).
100 PRINT "HOW MANY ENTRIES?"
110 INPUT N
120 FORI=1 TO N
130 POKE 16478,I+37
140 INPUT A$
150 NEXT I
200 FOR I=1 TO N
210 FOR J=1 TO N-I
220 POKE 16537,J+37
230 POKE 16540,J+38
240 IF B$>C$ THEN GO SUB 900
250 NEXT J
260 NEXT I
300 FOR I=1 TO N
310 POKE 16584,I+37
320 PRINT D$
330 NEXT I
340 STOP
900 POKE 16658,J+37
910 POKE 16664,J+37
920 POKE 16667,J+38
930 POKE 16673,J+38
940 LET Z$=E$
950 LET F$=G$
960 LET H$=Z$
970 RETURN
Notes:
100-150: Get the words.
130: Changes A$.
200-260: Sort the words.
220: Changes B$.
230: Changes C$.
300-340: Display the words.
310: Changes D$.
900: Changes E$.
910: Changes F$.
920: Changes G$.
930: Changes H$.
900-970: Move the words.
Program 2: Card Strings (4K ROM; 1K RAM)
100 PRINT "PLAYERS(2-4)?"
110 INPUT N
120 FOR I=1 TO N
130 PRINT "PLAYER#";I
140 POKE 16489,I+37
150 INPUT A$
160 NEXT I
190 LET E=20+10*N
200 DIM C(E)
210 FOR I=1 TO E
220 LET P=RND(E)
230 IF C(P)>0 THEN GO TO 220
240 LET C(P)=I
250 NEXT I
300 LET P=E-19
400 FOR I=1 TO N
410 CLS
415 LET T=0
420 POKE 16616,I+37
430 PRINT B$
435 PRINT
440 FOR J=0 TO 9
450 PRINT J;
452 FOR L=0 TO C(J*N+I)/N/4
454 PRINT "#";
456 NEXT L
458 PRINT C(J*N+I)
460 IF T=0 THEN GO TO 490
465 IF T>9 THEN GO TO 800
470 IF C(J*N+I)>C(J*N+N+I) THEN RETURN
480 LET T=T+1
490 NEXT J
495 PRINT
500 LET Y$="SHOW"
510 PRINT Y$;"#CARD=";C(P);"#TAKE?"
520 INPUT Z$
530 IF Z$>"X" THEN GO TO 600
540 IF Y$="DRAWN" THEN GO TO 650
550 LET P=P+1
560 IF P>E THEN LET P=E-19
570 LET Y$="DRAWN"
580 GO TO 510
600 PRINT "PLACE?"
610 INPUT J
620 LET T=C(J*N+I)
630 LET C(J*N+I)=C(P)
640 LET C(P)=T
650 CLS
660 LET T=1
670 GO SUB 430
680 INPUT Z$
700 NEXT I
710 GO TO 400
800 POKE 16988,I+37
810 PRINT C$;"#WINS"
Notes:
100-160: Get the players.
190: E=total number of cards.
200-250: Shuffle the cards.
300: P=pointer to show card.
410-490: Display a player’s cards.
452-458: Spaces each card over by magnitude.
465-480: Part of winner test subroutine.
500-580: Players pick their cards.
600-640: Chosen card put in “rack.”
650-680: Test for winner. Display cards in order so far.
700: Next player’s turn.
710: Back to player 1.
800-810: Print the winner.
[Figure: Sample Run: four screens showing player entry, the players’ card racks, and SHOW CARD / DRAWN CARD / PLACE? prompts]
8K ROM Versions
Although the article is intended to help 4K ROM users, we thought the 8K ROM users might like to use the programs so the 8K ROM Versions are also given below in Programs 3 and 4.
Program 3: Alphabetize (8K ROM; 1K RAM).
100 PRINT "HOW MANY ENTRIES?"
110 INPUT N
115 DIM A$(N,6)
120 FOR I=1 TO N
140 INPUT A$(I)
150 NEXT I
200 FOR I=1 TO N
210 FOR J=1 TO N-I
240 IF A$(J)>A$(J+1) THEN GOSUB 900
250 NEXT J
260 NEXT I
300 FOR I=1 TO N
320 PRINT A$(I)
330 NEXT I
340 STOP
940 LET Z$=A$(J)
950 LET A$(J)=A$(J+1)
960 LET A$(J+1)=Z$
970 RETURN
Program 4: Card Strings (8K ROM; over 1K RAM).
100 PRINT "PLAYERS(2-4)?"
110 INPUT N
115 DIM A$(N,10)
120 FOR I=1 TO N
130 PRINT "PLAYER ";I
150 INPUT A$(I)
160 NEXT I
190 LET E=20+10*N
200 DIM C(E)
210 FOR I=1 TO E
220 LET P=INT (RND*E)+1
230 IF C(P)>0 THEN GOTO 220
240 LET C(P)=I
250 NEXT I
300 LET P=E-19
400 FOR I=1 TO N
410 CLS
415 LET T=0
430 PRINT A$(I)
435 PRINT
440 FOR J=0 TO 9
450 PRINT J;
452 FOR L=0 TO C(J*N+I)/N/4
454 PRINT " ";
456 NEXT L
458 PRINT C(J*N+I)
460 IF T=0 THEN GOTO 490
465 IF T>9 THEN GOTO 800
470 IF C(J*N+I)>C(J*N+N+I) THEN RETURN
480 LET T=T+1
490 NEXT J
495 PRINT
500 LET Y$="SHOW"
510 PRINT Y$;" CARD=";C(P);" TAKE?"
520 INPUT Z$
530 IF Z$>"X" THEN GOTO 600
540 IF Y$="DRAWN" THEN GOTO 650
550 LET P=P+1
560 IF P>E THEN LET P=E-19
570 LET Y$="DRAWN"
580 GOTO 510
600 PRINT "PLACE?"
610 INPUT J
620 LET T=C(J*N+I)
630 LET C(J*N+I)=C(P)
640 LET C(P)=T
650 CLS
660 LET T=1
670 GOSUB 430
680 INPUT Z$
700 NEXT I
710 GOTO 400
810 PRINT A$(I);" WINS"
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.