Byte-sized delights that are guaranteed to ward off hysteria and melancholy!
There are a couple of obsolete categories of programs that I find intriguing, called “one-liners” and “two-liners.” These are programs that are written using only one or two lines of code which, ideally, do something interesting. Nibble Magazine, popular in the 80’s for its lengthy printouts of Applesoft and machine language programs that readers could manually type in, gave me my first glimpse into the world of one- and two-liners. It held a contest where programmers could submit their miniature programs, and the editors would publish the winning programs every month.
Since the only limitation was that these programs fit into one or two lines of code, the variety of topics that they covered was broad. Some programs did “useful” things like calculate gas mileage, while others bounced a ball around the screen. Still others did amazing things like fill the display with three-dimensional imagery or play music… all in one or two lines of code!
While my adolescent mind was blown on a monthly basis by these contest winners, I was also learning a great deal about algorithms and code optimization by picking apart these programs to understand how programmers accomplished these feats. Some of those learnings I still carry with me today. While the tips and tricks below are very specific to Applesoft, there is probably some broader lesson to be learned about joy and programming. I’ll let you figure that part out for yourself. :)
We’re trying to make Applesoft do interesting things in one or two lines of code, but how long can a single line of code be? When typing in a really long line at the Applesoft prompt, you may have noticed that the computer starts beeping at character 249. This is the system warning you that you’re approaching the max line length, and that your input will be rejected when you go over 255 characters. What’s really interesting, and what I was recently surprised to learn was that Applesoft actually rejects all input after 239 characters.
TIP: You are limited to 239 characters per line. One nice thing about this number is that it comes out to exactly 6 full rows of 40-column text (including the “]” prompt). If you go over 6 rows, you’ll know you’ve gone over budget.
This one’s super easy. Line numbers count toward your 239-character budget, so stick to single-digits. This should be easy since you’re optimizing for just one or two lines.
TIP: Keep it simple, start your program at line 0. If you’re writing a two-liner, impress your friends and make the second line number 1.
Another easy one: Don’t use whitespace. With few exceptions, Applesoft is really good at parsing code with no spaces.
0FORN=0TO1:?".";:N=PEEK(-16384)>128:NEXT:GETA$
TIP: Don’tuseanyspaces “Except in strings.”
NOTE: I may include spaces for clarity in some of the examples below.
Now let’s jump into some of the tricky tricks. Your old friends GOTO
, GOSUB
, and IF
are not welcome in this dojo… at least, not in most of the cases where you would typically use them. This is not to say that you should never use them, but that you’ll be less likely to use them for your one- and two-liners.
For example, let’s say you’re writing a program that uses the hi-res screen to do something interesting infinitely…
(The spaces are just to make it easier to visually parse. Don’t do this in production code!)
0 HGR : { DO INTERESTING THINGS HERE }
You can’t use GOTO 0
to loop the interesting part because it will clear the screen every time. What are we to do?
FOR
is an obvious choice for repeating work a specific number of times, but it might not be so obvious if you want to repeat work indefinitely in a single line of code.
HOME : FOR N = 0 TO 1 : PRINT N; : N = 0 : NEXT
This looks pretty simple, but let’s examine what this line does.
HOME
clears the screen.FOR
loop is initialized and N
is set to 0.N
is set to 0 (which it already is).NEXT
increments N
to 1 and tests to see if it’s higher than one. It’s not, so it loops.N
is set back to 0.As you probably guessed, the output of this line of code is: 0111111111111111111...
(repeating “1” forever)
TIP: Modify the iterator variable in FOR-NEXT
loops to conditionally repeat code.
Let’s modify the previous example to stop when the user presses any key, instead of repeating forever:
FOR N = 0 TO 1 : PRINT N; : N = PEEK(-16384) > 127 : NEXT : POKE -16368,0
What’s going on here? Instead of just resetting N to 0 every time like in the previous example, we’re setting it to the result of the expression PEEK(-16384) > 127
which incidentally returns 1 if a key is pressed and 0 if no key is pressed. BTW, If we don’t subsequently POKE -16368,0
, the keypress will remain in the buffer until some inopportune time, such as at the next Applesoft prompt, where it’ll just dump out and look ugly, forcing the user to arrow-key to the left. I’ll cover interesting PEEKs and POKEs later.
Let’s examine the last example again, except as a program starting with line 0.
0 FOR N = 0 TO 1 : PRINT N; : N = PEEK(-16384) > 127 : NEXT : POKE -16368,0
That’s pretty short, but it contains a lot of unnecessary white space. Let’s remove it.
0FORN=0TO1:PRINTN;:N=PEEK(-16384)>127:NEXT:POKE-16368,0
Better. But every Applesoft aficionado knows PRINT
is just long-hand for ?
.
0FORN=0TO1:?N;:N=PEEK(-16384)>127:NEXT:POKE-16368,0
Also, negative POKEs and PEEKs have a positive representation. Just add 65536.
0FORN=0TO1:?N;:N=PEEK(49152)>127:NEXT:POKE49168,0
Can we do better? You betcha.
0FORN=0TO1:?N;:N=PEEK(-4^7)>127:NEXT:POKE49168,0
Every character counts!
0FORN=0TO1:?N;:N=PEEK(-4^7)>127:NEXT:GETA$
Since we only exit the loop when a key is pressed, we know that the GET
will immediately consume that keypress and not pause the program. We’ve gone from 70 characters to 42 characters!
Let’s look at the progression again.
0 FOR N = 0 TO 1 : PRINT N; : N = PEEK(-16384) > 127 : NEXT : POKE -16368,0
0FORN=0TO1:PRINTN;:N=PEEK(-16384)>127:NEXT:POKE-16368,0
0FORN=0TO1:?N;:N=PEEK(-16384)>127:NEXT:POKE-16368,0
0FORN=0TO1:?N;:N=PEEK(49152)>127:NEXT:POKE49168,0
0FORN=0TO1:?N;:N=PEEK(-4^7)>127:NEXT:POKE49168,0
0FORN=0TO1:?N;:N=PEEK(-4^7)>127:NEXT:GETA$
If you’ve been running each step in our optimization, you may have noticed that computing -4^7
is a bit slower than the previous iterations. It saves a character, but at the expense of speed. This is a trade-off that I usually don’t make unless I absolutely need that one extra character, but this section is about optimizing the number of characters.
Can we make it shorter? I’m not sure, but let’s try some stuff…
Our infinite looping FOR-NEXT
construct is serving us well, but it’s worth noting that counting from 0 to 1 is fairly arbitrary. In our case, the first factor could be any number less than one, such as 0 or -10 or -1000.
0FORN=-1000TO1:?N;:N=PEEK(-4^7)>127:NEXT:GETA$
This really doesn’t do anything for us, but it illustrates an important aspect of optimization: Thinking creatively and keeping an open mind. What happens if we change the second factor to 2?
0FORN=0TO2:?N;:N=PEEK(-4^7)>127:NEXT:GETA$
Uh oh. Our program is broken. Pressing a key no longer ends it. :( Why? Because we changed the exit criteria. The second factor determines whether the NEXT
loops or continues to the next statement. Remember, NEXT
increments and then tests. We’re setting N
to 0 or 1, so the subsequent NEXT
will increment it to 1 or 2. It will never be greater than 2, which is the second factor in our FOR
statement, therefore the loop will never end.
Even though we broke our program, this is interesting.
0FORN=0TO127:?N;:N=PEEK(-4^7):NEXT:GETA$
Instead of testing if the keypress strobe PEEK(-4^7)
is greater than 127, why not let NEXT
do the test for us? Note the program now repeatedly outputs the result of the PEEK until a key is pressed, which sets N
to the ASCII code of the pressed key plus 128, which causes the NEXT
to continue. This program started at 75 characters and we managed to shave off a whopping 35 characters! The final program is 40 characters. Can you make it even shorter?
Pardon the dust. This section is incomplete.
Peek/Poke | Function |
---|---|
X = PEEK(49152) | Strobes the keyboard |
POKE 49168,0 | Clears the keyboard strobe |
X = PEEK(49200) | Clicks the speaker |