robotfindskitten, part 1
robotfindskitten, part 1. Dan’s MEGA65 Digest for October 2023.
A new month, a new feature, a new game, a new demo, and a coding exercise that really brings the room together. Let’s dig in!
Available to test: New keyboard scanner
We’re getting closer to having a complete release candidate, with features being finalized and bug fixes piling in. There’s one new feature that’s near and dear to my heart, and I’m thrilled to be able to share it with anyone up for early beta testing.
The very first thing I noticed when I got my MEGA65 is how the typing experience felt nostalgic. Even with the new mechanical key switches and the 40 MHz CPU, the ability for the computer to recognize key presses felt exactly like a Commodore 64 did back in the day: sluggish and imprecise. I like nostalgia as much as anybody, but I type much faster today than I did when I was nine years old. I wished my MEGA65 could handle fast typing more like a modern computer, so I could enjoy on-device composition without slowing myself down. It was one of the first feature requests that I filed with the MEGA65 team.
Paul and company started working on an idea for fast typing, and an early version of the chipset support required made it into the core last year. The feature wasn’t wired up to the KERNAL ROM, and only a few built-in applications used it. I brought it up again with the team this summer, and we had many design discussions and tried several ideas. I learned a bit of FPGA coding, and even built a high-speed Linux PC just to build and test new cores and ROMs.
I’m proud to present the all-new MEGA65 hardware-accelerated keyboard scanner. Using the latest development core and ROM beta release, you can now enjoy a typing experience that is more accurate and more reliable for fast typists throughout the BASIC screen editor and many applications. These changes will be in the v0.96 release, and you can test it today and file any bugs you find. I also wrote a test plan and detailed description of how it works.
I plastered this all over the instructions, but I’ll repeat it here: the latest ROM beta versions (920387 or later) require the latest development core. If you use the newer ROM with an older core, typing won’t work. You can safely use older ROMs with the newer core, so you can revert to the legacy keyboard scanner at any time if you encounter any issues just by going back to an older ROM (920386 or earlier). Also, the latest ROM betas will not work with the Xemu emulator until emulation for the new core feature has been added (hopefully soon), so stick with 920386 or earlier in Xemu for now.
For me, the difference with the enhanced typing quality is like night and day. On-device programming and other typing applications are much more usable, and I just enjoy using the computer much more, confident that I won’t have to struggle with missed keystrokes. Try it out, and let me know what you think!
Classy, by deathy
Classy, by deathy, is a new match-3 game for the MEGA65. Careful with this one: it’s addictive! A joystick in either port or the keyboard sets you on your tile swapping journey. If the game is still running, it means you still have moves remaining. Keep looking!
MEGApple, by MirageBD
The MEGA65 gets its Bad Apple!!
MEGApple is MirageBD’s MEGA65 version of the popular demo challenge: recreate the shadow-art music video of the Japanese pop song, “Bad Apple!!” The original video was a collaboration of animators on the Japanese website Nico Nico Douga, based on the pop remix by nomico of a track from the video game Touhou Fantasy Land: Lotus Land Story. Over a decade later, it persists as a popular meme to recreate the music video in unusual media, including (but not limited to) retro computers.
To run MEGApple, download and expand the archive, then copy the files to the root of your SD card. Start your MEGA65, then type: MOUNT "MEGAPPLE.D81":RUN "*"
The program on the D81 disk image loads the data from the other larger files.
Xevious, new arcade core by muse
muse has a new arcade cabinet core for us! Xevious (1982) and Super Xevious (1984) are early vertical scrolling shooter games by Namco. Fly your Solvalou starship over the planet’s surface to stop the Xevious forces by both land and sky. muse’s version supports a one-button joystick with a clever adaptation: press the button quickly to shoot at airborne enemies, and hold the button to drop bombs on turrets.
As with the Galaga core, the installation instructions include searching for and downloading the original Xevious ROM files, running a Python script to validate the files and prepare a folder that you copy to your MEGA65’s SD card with the path /arcade/xevious
, then installing the core in an available core slot. See the Discord announcement for additional notes.
robotfindskitten
“Yet another zen simulation.” — robotfindskitten.org
In robotfindskitten, you are a robot, who finds a kitten.
robotfindskitten was originally written by Leonard Richardson in 1997 for DOS, as his submission to the robotfindskitten contest put on by the webzine “Nerth Pork.” It took first prize. There were no other entries, but there didn’t need to be. The theme had found its perfect expression on the first try.
In the decades that followed, many have made their own versions of the simulation, on many interactive platforms. And now, it is your turn.
Richardson’s version depicts the robot with a single colored ASCII character on a monospaced field that spans the screen. The field is populated sparsely with other colored ASCII characters that represent many Non-Kitten Items (NKIs), and one kitten. To the robot, all items, including the kitten, appear as random colored characters. The operator uses cursor keys to move the robot around the field. When the robot comes into contact with an item, a message is printed describing the item. If the item is the kitten, the experience ends in joyful celebration.
You can play robotfindskitten in your browser. Or, you can install a version for a modern terminal for Linux (sudo apt install robotfindskitten
) or macOS (brew install robotfindskitten
) or many other platforms. (Some versions are survived only by their archived screenshots.) Or you can build the Linux version from source. Or you can check out the Ultimate robotfindskitten Fan Site, by Leonard himself.
Or you can make your own. Here are some techniques that you might use to rise to this challenge in BASIC 65. Next month, we’ll look at how to do similar things in assembly language.
Display
You will need to control what is displayed on the screen, so the operator can see the robot and the items.
To clear the screen of all text, you can use the SCNCLR
command. To set the color of text to be printed (from 0 to 31): COLOR number
. Similarly, to change the color of the border or background: BORDER number
or BACKGROUND number
. To set the position of where the next text will be printed: CURSOR column, row
. To print a string of text at the current cursor position: PRINT string
.
One word of caution with PRINT
: if the string you are printing extends beyond the right edge of the screen, the screen below that point will scroll down by one line. This probably isn’t what you want in this exercise, so take care with the length and position of your strings.
To plot a single character at a set of coordinates, you can assign the screen code of the character to the T@&
array, and its color to the C@&
array. These are two dimensional arrays, with the first index referring to the column and the second index referring to the row. T@&(0,0)=1
plots the letter A
to the leftmost topmost position. (1 is the screen code for the A
character. Notice that screen codes are not PETSCII codes.) C@&(0,0)=5
paints it green. You can read screen codes and colors from these arrays as well: IF T@&(0,0)=1 THEN ...
and so forth.
Recall that PETSCII codes in strings give you further control of the display via the PRINT
command. PRINT CHR$(14)
switches the display to the lowercase character set. Be aware that not all PETSCII graphics characters are available in the lowercase set.
Here is a short program that clears the screen, switches to lowercase, and plots all of the available screen codes:
100 COLOR 1
110 SCNCLR
120 PRINT CHR$(14)
130 FOR R=0 TO 3
140 FOR C=0 TO 63
150 T@&(C,R)=R*64+C
160 NEXT C
170 NEXT R
180 CURSOR 0,5
Input
To accept keyboard input, use either GET variable
or GETKEY variable
. These both take a variable name as their parameter. Given a string variable (such as A$
), the key being pressed is loaded into the variable as a single-character string. Given a number variable (such as A
), it is loaded as a PETSCII code. GETKEY
pauses until a key is pressed. GET
will not wait: if no key is pressed, it assigns an empty string or a zero, and moves on. If you want GET
or GETKEY
to provide the PETSCII code as a number, be sure to use a byte type variable, so that the number is in the range 0 to 255: GET K&
.
10 GETKEY K&
20 PRINT K&
30 GOTO 10
Alternatively, you can accept joystick input using the JOY()
function. The function takes the port number as an argument (1 or 2), and returns a number 0 through 8 indicating the state of the joystick. 0 says the joystick is centered. All other numbers indicate a direction, starting with 1 for “up” and going clockwise: 2 for “up-right,” 3 for “right,” and so forth.
Chaos
The items are assigned random characters, colors, and positions, so you’ll need a way to generate randomness. The RND()
function generates numbers using a pseudo-random number generation algorithm. The function takes an argument that determines the “seed” for the algorithm. For most purposes, an argument of 1 suffices: RND(1)
. The function evaluates to a random fractional number between 0 and 1. To generate numbers within a range of integers, multiply the result by the size of the range, then take the INT()
of the result to lop off the fractional part. INT(RND(1)*80)
generates a random integer between 0 and 79.
If you want the range of possible values to start at a value other than zero, do the inner multiplication by the size of the range, then add the starting value. INT(RND(1)*77)+1
generates a random integer between 1 and 77.
The following program plots randomly colored random characters when one of the cursor keys is pressed. See if you can figure out what the variables CL
, CH
, RL
, and RH
do.
100 SCNCLR
110 CL=0:CH=79:RL=0:RH=24
120 GET K&
130 IF K&<>17 AND K&<>29 AND K&<>145 AND K&<>157 THEN 120
140 IF K&=17 THEN RL=12 : REM CURSOR DOWN
150 IF K&=29 THEN CL=40 : REM CURSOR RIGHT
160 IF K&=145 THEN RH=12 : REM CURSOR UP
170 IF K&=157 THEN CH=40 : REM CURSOR LEFT
180 C=CL+INT(RND(1)*(CH-CL))
190 R=RL+INT(RND(1)*(RH-RL))
200 T@&(C,R)=INT(RND(1)*256)
210 C@&(C,R)=INT(RND(1)*16)
220 GOTO 110
Like the Commodore 64, a positive argument generates a new number based on the previously generated number. Unlike the Commodore 64, the algorithm is initially seeded from the MEGA65’s Real-Time Clock during boot, so you are unlikely to see the same sequence twice with RND(1)
.
Tip: For RFK, you probably want to prevent randomly selected values from repeating. A simple way to avoid this is to keep track of previously selected values, such as in an array, and scan the array each time a new value is chosen. If it’s in the array, restart the process to pick another value. Once a unique value is found, add it to the end of the array. In my version, I used this technique to avoid two items from appearing at the same location, and to ensure that each item uses a unique PETSCII character and a unique description.
Calm
To animate the joyful celebration at the end, you’ll need a way to pause between frames of an animation. The SLEEP seconds
command pauses the program for the given number of seconds. The number can be fractional.
10 FOR X=1 TO 5
20 PRINT CHR$(211);
30 SLEEP 0.6
40 NEXT X
50 PRINT
Data
Each Non-Kitten Item in robotfindskitten gets assigned a description at random when the item is created. One way to put a large list of descriptions in a BASIC program is with DATA
statements. These statements don’t do anything when encountered by control flow. Instead, they make values available to the READ
statement, which finds the next unread value and puts it in a variable.
A traditional BASIC technique is to read the number of elements from the first DATA
statement, DIM
ension an array of that size, then READ
that many elements into the array. The program can then access any string from the array by its number index. This is a bit wasteful. It keeps two copies of the list in memory: one in the BASIC program listing, and another in variable memory. It also introduces a delay at the beginning of the program to load all of the elements into the array. The primary advantage is that each element can be accessed instantly by index.
In BASIC 65 (and BASIC 7 for the Commodore 128), there’s a better way: use sequential line numbers for the DATA
statements, then use the RESTORE
command to set the READ
pointer to a given line number. RESTORE
accepts a number expression for the line number, so you can select a string programmatically by number.
10 READ N
20 K=INT(RND(1)*N)+1
30 RESTORE 1000+K
40 READ NK$
50 PRINT NK$
60 GETKEY A$
70 GOTO 20
1000 DATA 5
1001 DATA "THAT'S JUST AN OLD TIN CAN."
1002 DATA "IT'S AN ALTAR TO THE HORSE GOD."
1003 DATA "A BOX OF DANCING MECHANICAL PENCILS. THEY DANCE! THEY SING!"
1004 DATA "IT'S AN OLD DUKE ELLIGTON RECORD."
1005 DATA "A BOX OF FUMIGATION PELLETS."
A DATA
statement has a special syntax to allow for string values that contain almost any character: values are separated with commas, and a value that starts with a double-quote contains every character (including commas) up to the next double-quote. This requires special handling if you want a string value to contain a double-quote character. Because NKI descriptions only use letters, numbers, and punctuation, one solution is to use a special character as a substitute for double-quotes, then replace it in code:
41 FOR I=1 TO LEN(NK$)
42 IF MID$(NK$,I,1)="£" THEN MID$(NK$,I,1)=CHR$(34)
43 NEXT I
1000 DATA 6
1006 DATA "IT'S A DVD OF £CROUCHING MONKEY, HIDDEN KITTEN£, REGION ENCODED FOR THE MOON."
You can use the original list of 406 NKI descriptions, or write your own. The truly patient can type all of these into DATA
statements by hand, a zen experience of its own. Or you can use one of these that I made for you:
- nkis_upper.bas / nkis_upper.prg : uppercase character set
- nkis_lower.bas / nkis_lower.prg : lowercase character set
Structure
Your program will have at least a few phases of operation, such as one to decide on the locations and appearances of the items, and another where the player is moving the robot. Each phase will be a section of lines in your program. BASIC executes lines in a given section to produce the experience of that phase. To transition from one phase to another, the program directs BASIC to GOTO
a line in the appropriate section.
I based my RFK experience on Richardson’s version, which can be described as four phases:
- Introduction. Display an intro message, then wait for a keypress. When a key is pressed, proceed to phase 2.
- Initialization. Set up and draw the items on the screen, set the initial robot position. When complete, proceed to phase 3.
- Game loop. Listen for player input, move the robot, display NKI descriptions. When the robot finds the kitten, proceed to phase 4.
- Ending. Animate the joyful experience, then wait for a keypress. When a key is pressed, return to phase 1.
My program code also ends with the NKI descriptions as a fifth section of data statements. Control never reaches this section, because DATA
statements do not need to be performed like other commands. (The READ
statement in an earlier section accesses this data.)
One of the best things about BASIC is how it makes it easy to write and test each section separately. To start a section from the READY.
prompt, provide the first line number to the RUN
command:
RUN 300
You can temporarily add a STOP
statement in the program to cause execution to return to the READY.
prompt at that point. You can use commands to inspect the state of program variables, such as: ?NK$
When you are ready to resume execution of the program, type the CONT
command. You can even assign new values to variables while paused, and it’ll use the new values when you continue the program. Don’t forget to remove the STOP
statements when you no longer need them.
When you’re finished
As amusing as it would be for everyone to upload their RFK experiences to Filehost, that might not be the most polite use of the site. Let’s do this instead: send me a photo or screenshot of your robotfindskitten experience. I will try to feature a selection of submitted photos in an upcoming issue of the Digest.
If you have any questions about BASIC programming or would like to discuss the exercise, let’s meet up in the #basic
channel of the Discord chat. I’m usually there.
Next month, I’ll share my BASIC RFK implementation, and we’ll look at similar tools for making an RFK game in assembly language. See you then!
— Dan