2020-04-30 – Added missing semicolon in code and updated example.
NOTE: This article was originally written a few years ago, so some references may be out of date.
I have been enjoying working on the SirSound project the past month, as well as some fun challenges at my day job. Every now and then I run into something I’d like to do that is not doable in C, or doable but not proper to do in C, or … maybe doable. It’s sometimes difficult for me to find answers when I do not know how to ask the question.
With that said, I have a C question for anyone who might be able to answer it.
Using my multi-track music sequencer as an example, consider representing data like this:
It’s easy to create a sequence like this in C. Here’s some pseudo code:
track1 = { C, C, C, C };
track2 = { E, E, E, E };
sequence1 = { track1, track 2};
I thought there might be a clever way to do all of this with one initializer. If I treat the data like nested XML (like the first example), I thought it might be possible to do something like this:
…though the address of “light” is quite different than the address of a structure which contains a pointer that should point to “light”.
I was going to make each one a pointer to an array of words, so I could have a tree of words like the earlier example (with kitchen/light and kitchen/fan):
I have been dealing with some WordPress problems lately so I have been trying to restore everything to working condition. You may encounter site downtime while I am working on this. Don’t panic. I’ll get it going again, shortly…
Making BASIC run faster is hard to automate, but there have been some attempts to do this over the years.
Carl England CRUNCH
No, it’s not that cereal you remember from Saturday morning TV ads. It’s one of the coolest utilities ever created for the CoCo. Carl England wrote quite a few of those, actually.
Carl England was the creator of my all-time favorite Disk Extended BASIC program – Super Boot. It was a superb “type DOS and auto run your program” utility that added many neat features. I have it on many of my RS-DOS disks.
Carl also created THE DEFEATER, a copy utility that could duplicate any copy protected disk. It did not crack the software – it just cloned it, making a duplicate copy protected disk.
Carl also showed off a scanner attachment at the 1990 Atlanta CoCoFest that turned a Tandy DMP printer into scanner! But that’s a story for another time…
Make BASIC small again
Today I want do discuss one of Carl’s programs called CRUNCH. It is a machine language program that will pack a BASIC program to be as small as possible. It does this by removing REMs and unnecessary spaces. It also removes other unnecessary things like “IF X THEN GOTO 100” which could be written just as “IF X THEN 100”. It will even remove trailing quotes at the end of a line (which looked weird, but saved a byte).
But the most important thing it does is pack (er, crunch?) a BASIC program into as few lines as possible. It will take a program like this:
0 REM
1 REM GUESS.BAS
2 REM
3 REM BY ALLEN HUFFMAN
4 REM
5 REM GENERATE RANDOM NUMBER
10 N=RND(100)
15 REM DISPLAY INSTRUCTIONS
20 PRINT "I AM THINKING OF A NUMBER"
30 PRINT "BETWEEN 1 AND 100."
35 REM ASK FOR A GUESS
40 PRINT "WHAT IS YOUR GUESS";
50 INPUT G
55 REM IS GUESS TOO HIGH?
60 IF G > N THEN 100
65 REM IS GUESS TOO LOW?
70 IF G < N THEN 200
75 REM IS GUESS CORRECT?
80 IF G = N THEN 300
85 REM REPEAT
90 GOTO 35
100 REM TOO HIGH
110 PRINT "TOO HIGH!"
120 GOTO 35
200 REM TOO LOW
210 PRINT "TOO LOW!"
220 GOTO 35
300 REM CORRECT
310 PRINT "CORRECT!"
320 END
…and turn it into something like this:
10 N=RND(100):PRINT"I AM THINKING OF A NUMBER":PRINT "BETWEEN 1 AND 100.
40 PRINT"WHAT IS YOUR GUESS";:INPUTG:IFG>N THEN100
70 IFG<NTHEN200
80 IFG=NTHEN300:GOTO 40
110 PRINT"TOO HIGH!":GOTO35
210 PRINT"TOO LOW!":GOTO35
310 PRINT"CORRECT!":END
…except you generally can not list the program afterwards because CRUNCH will combine lines up to the maximum allowed (250 bytes or so?) and create lines too long to be EDITed or LISTed manually.
And, look at line 70 and 80. If you type them, you have to have a space after the variable in “IFG<N THEN…” because the tokenizer needs to know where the variable ends and the next keyword starts. CRUNCH can remove that space, which is impossible to do when typing that in by hand.
Here is what it looks like in operation on the GUESS.BAS program above:
Carl England’s CRUCCH program, before.
You can select a specific operation to do, or choose 7 and do them all:
Carl England’s CRUCCH program, after.
In this example, it changed a 519 byte BASIC program into 214. And, if you renumber the results by 1s starting at line zero (RENUM 0,0,1), that will save an addition 9 bytes because “GOTO30” (two digit line numbers) takes up more bytes than “GOTO9” (single digit line numbers).
CRUNCH is pretty amazing. And, you can download it today for free! It is one of the extra utilities in Carl’s DEFEATER disk copy utility package:
While that archive says CoCo 3, CRUNCH itself will work on a CoCo 1/2. At least, it seems to in my testing using the Xroar emulator.
Try CRUNCH out on some of your programs and share the results in the comments. Just keep in mind that it will destroy your ability to edit the program! Save your crunched program under a different name! You can then distribute that crunched copy, or be nice and give folks both the original (hopefully easier to read) and crunched (smaller and faster to run).
GEnie was an text-based online server owned by General Electric. I recently found the nondisclosure agreement I signed to become an assistant at the Tandy RoundTable. I was COCO-SYSOP there until the service shut down in 1999. Apparently GEnie (which became Genie by then) was not Y2K compliant… Enjoy!
Non-Disclosure Agreement
Between:
Allen C. Huffman Effective Date: March 15th, 1991
xxxxxxxxxxxxxx
Lufkin, Texas 75901
(hereinafter referred to as "Assistant SysOp" or "Assistant"),
And: General Electric Company, a New York corporation, acting through its GE
Information Services Division, (hereinafter referred to as "GE"), 401 N.
Washington Street, Rockville, Maryland 20850.
WHEREAS, GE operates remote access computer systems through which it offers
various network-based information services, both directly and through
distributors;
WHEREAS, one of the services which GE offers by such means is a consumer
information service known as GEnie Service (hereinafter referred to as "the
Service");
NOW, THEREFORE, GE and Assistant SysOp hereby agree as follows:
A: Either GE or Assistant SysOp may disclose to the other certain
information which the disclosing party deems to be confidential and
proprietary ("Information"). Such Information shall be clearly and
conspicuously marked as confidential and proprietary at the time of first
disclosure to the receiving party. Such Information would include, but is
not limited to, documentation related to the Product and the Service and
business information of GE or Assistant SysOp which is not generally
available to the public.
B: The receiving party shall exercise reasonable care to prevent
disclosure of or use for any purpose unrelated to use on the Service or to
the evaluation of the Product for suitability on the Service, at any time
prior to the expiration of three (3) years following the termination of this
Non-Disclosure Agreement, of any Information which it receives from the
other party pursuant to and in accordance with the terms of this
Non-Disclosure Agreement. The receiving party shall also require its
employees and agents to similarly restrict use and disclosure of such
Information. The receiving party, however, shall not be required to keep
confidential any Information which is or may become publicly available
without fault on its part; is already in the receiving party's possession
prior to receipt from the disclosing party; is independently developed by
the receiving party; is disclosed by the disclosing party to third parties
without similar restrictions; or is rightfully obtained by the receiving
party from third parties without restriction.
GENERAL ELECTRIC COMPANY
By: __________________________ By: __________________________
Robert Chiappone Allen C. Huffman
Title: Manager, Product Marketing Title: Tandy RT SysOp
GEnie
Date: __________________________ Date: __________________________
GEnie User Number: _____________
RT, Game or Product: ___________
NOTE: This document is from 1985. It was the “official” instructions for a nighttime game of tag we played when I lived in Broaddus, Texas. I recently found it on some old Deskmate disks and thought it was fun enough to share… If you decide to play, let me know how you like it! We had a blast.
BEAMSCAPE – Official Rules
You quickly dash around a corner in an effort to evade your pursuer. You press yourself against the wall, hoping you won’t be sighted. Your heart pounds as you hear the sound of running feet. Suddenly, you are engulfed in a bright light. You have been spotted. You start to run, but there is nowhere to hide. A loud voice yells out:
“One one-thousand! Two one-thousand! Three…”
Welcome to BeamScape! BeamScape is the classic game of hide-and-seek with a new twist. First, it is played at night with the aid of flashlights. Secondly, the light used by the Beamer (the person who is “it”) can be used to tag out other players.
BeamScape is more like a cross between tag and military wargames. The many strategies used in playing make it a fun and exciting “sport.”
GENERAL INFORMATION
In order to play BeamScape, you will need a high-powered flashlight such as the Mag-Light, which uses a Krypton bulb and can be adjusted to a small beam. The official number of players is five, although any number of three or more can play. The more, the better!
GAME RULES
Most of the rules of BeamScape are similar to those of hide-and-seek. The person who is it, called the “Beamer,” starts counting while the players, known as “Runners,” go hide. After counting to a preselected number, which will vary depending upon the playing area, the Beamer then begins his persuit.
In order to get someone, the Beamer must either touch them, or catch them in the light. If the Beamer gets the light on a Runner, he must keep it on them for five seconds while couting “One one-thousand, two one-thousand,” and so forth up to five. If the runner gets out of the light in that time, counting must begin again at one. Once a Runner has been “tagged,” he becomes a Helper and is alowed to aid others in getting to the base. A helper cannot, however, purposely get in the way of the Beamer’s light to help a Runner get away.
Any runner who safely reaches base must remain there until the next round. A round begins after everyone has either reached base, or gotten caught. If all the Runners safely reach base, the game starts again with the same Beamer. If one or more Runners get caught, the last one tagged becomes the new Beamer for the next round.
Because this game is played at night without light, optional rules may allow every Runner to carry a small penlight to help them see. A regular sized flashlight should not be used since it’s light could confuse other Runners.
PLAYING AREA
The game field for BeamScape may be anywhere. Good locations are places where there are lots of obstacles, such as storage buildings and trees. Before playing, though, be sure that the area is clear of any dangerous locations, and that it is not in an area that might cause trouble with non-players. (It is quite suspicious to see many people running around an area at night with flashligts, so if you are playing on someones private property, be sure to let the owner know what is going on!)
Also, be sure to have all the boundries decided upon before playing and make sure the rules are clear to all players to help prevent any arguments caused by misunderstanding.
FINAL NOTES
The version of BeamScape which is presented here is a simple form designed to be played by many. A higher and more high-tech game may have options like two-way communicators with all the Runners, but not the Beamer. In fact, the rules of BeamScape were meant to set the basis for many different variations depending on the abilities of the players, so feel free to incorporate “home rules” to enhance the fun on BeamScape.
In this installment, I will walk through the code for my VIC-20 game Sky-Ape-Er. Thanks to a wonderful utility I mentioned in part one, I have this ASCII program listing. The utility replaces the special PETASCII control characters with {words} so they can be viewed on non-Commodore systems.
Sky-Ape-Er program listing
Initialization
1 POKE 808,100
That poke would “disable the RUN/STOP key, the RESTORE key, and the LIST command” to keep someone from being able to save a copy of it.
5 REM ************ *SKY-APE-ER* *******BY******* 6 REM*ALLEN HUFFMAN* ****************{$cc}
Comments, but with no dates. Thanks, me. One of my tapes had the game in various stages of completion, named “SKY-APE-ER 1”, “SKY-APE-ER 2” and “SKY-APE-ER 3”, but all my other tapes just called it SKY-APE-ER, and I’ve found at least two distinct versions of the game so far.
I am not sure what the {$cc} is at the end. From looking at the c64list.exe command I used to make this listing, it seems to be an unknown token. This might have been something I placed in that line to prevent listing, even after a LOAD without the POKE being executed. But, you can LIST 10- and still see it ;-)
S is the screen (level) to display. Sky-Ape-Er contained three screens, and a “game won” screen.
36879 is the border color, with 26 being red.
36878 is something to do with the music chip.
S1 is being set to the sound note location 36875.
775,200 disables LIST. Or maybe not. There was conflicting information in the magazine I looked that up in.
36869 enables the custom font characters.
Set up game screen
15 PRINT"{clear}":L=8118:B=7772:M=15
L is the starting memory location for the player.
B is the starting memory location of the chimp (B for barrel, I suppose, as this was inspired by Donkey Kong).
M is the character to POKE to the screen for the player (man). 15 is the letter”O”.
20 FORA=38400TO38905:POKEA,0:NEXTA
This clears the color for each character on the screen. The characters for the screen are stored in one block of memory, and the color of each character is stored in a different block of memory.
This clears the screen, then draws the letters that make up the ape character. The VIC-20 has no PRINT@ or LOCATE function, but you could embed cursor movement commands in the PRINT statements. Thus, it would “home” to the top fo the screen, move the cursor {down} one, then {right} one, set the color to {black}, print ABC, then go {down} one and {left} three times (cursor is now back under the A) and print DEF, and repeat for the other lines. The ape was 3×4 characters in size!
Display game screen
30 ONSGOTO35,45,55,500
This goes to the appropriate routine to display the current screen (level) based on the value of S. There were three screens. If S is 4, it goes to a “win” screen.
Line 80 pokes the Man on the screen at its Location. The Barrel is poked to a space, so erased. The S1 sound is stopped. Nothing would be playing the first time this code is ran.
If the Location is 7772 or 7749, we go to 400.
Move the chimp
85 B=B+23:IFPEEK(B)=0THENB=B-22:POKEB,16
This controls the movement of the enemy (chimp). B is the location. The VIC-20 screen is 22 characters per line, so incrementing B by 23 would move the position down and to the right.
The new location of B is checked to see if it is 0 (the brick). If it is, it moves the location back 22 (up one). Thus, the chimp always tries to go down and to the right, and if it can’t it just goes right.
The POKE is what puts the chimp character on the screen at that position.
From looking at this, it seems the chimp is not displayed if it goes down and to the right successfully. I wonder why? Bug? It would get displayed the next time. Or inteitonal? I’ll look into this.
Handle player falling
87 IFPEEK(L+22)=32THENPOKEL,32:L=L+22:POKEL,M
This looks at the block under the player. If it is 32 (empty), it erases the player and moves it down, then displays it at the new location.
This code is what makes the player “fall” when it jumps or, on screen 3, when it falls off the edge. Cool.
Make “tick” sound
90 POKES1,240:POKES1,0
S1 is the address of one of the sound channels (the VIC-20 has three musical channels, plus noise). This turns one on and off, making the “tick” sound.
I did not even consider being able to do background music for a game back then. I bet I could today, memory permitting, even in BASIC.
Collision detection (player and chimp)
95 IFB=LTHEN250
This is the collision detection. If the chimps location (B) is the same as the players Location, we go to 250 (death).
Reset chimp back to top
100 IFB=8118THENPOKEB,32:B=7772:POKEB,12
This checks to see if the chimp is at the bottom right of the screen.
If it is, it gets erased, and the position is reset back to the top left starting position and it is displayed there.
This homes the cursor, changes the color to blue, turns on reverse video, then displays “TIME:” and the current time value. The reason it goes reverse is because we are using a special character set that replaces the lower alphabet characters. In this mode, there is some memory thing that happens that wraps around the inverse video characters to the standard characters. When using special characters, you have access to the basic letters by using reverse. Apparently.
Keyboard input
115 K=PEEK(197):IFK=64THEN80
Memory location 197 is the current key being pressed. If no key is pressed, 64 is returned and the program goes back to the top and continues. (Move the chimp, update the time, etc.)
120 POKEL,32:POKES1,230
If there was input, then this line will erase the player, and turn on the sound chip to start making a tone.
Then we check to see if the player’s character is 14 (facing left). If it is, we move the player’s position up and to the left (22 characters per row, so -23).
If the player’s character is 15 (facing forward), we move the location up,
If the player’s character is 13 (facing right), we move the location up and to the right.
If the new location is in a wall, we go to 200 to fix that.
We set the player’s character to 15 (facing forward) so the jump will land facing that way.
We go back to 80 to continue the main game loop.
Handle jumping into wall collision
200 IFM=13THENL=L+21
210 M=15:GOTO80
This code takes care of jumping into walls.
If the character is facing right, then we move the position down and to the left back where it came from.
Else, we know the only other place it can hit a wall is to the right (wow, I was clever here!) so we just make the character face forward and go back to the main loop.
Death screen
250 FORA=1TO11:READN,L:POKES1,N:FORP=1TOL:NEXTP:POKES1,0:NEXTA 255 PRINT"{clear}{black}{rvrs on}{down} GUESS WHAT! YOU HAVE {down}JUST BEEN STRUCK DOWN {down} BY A LUCKY CHIMP!" 260 PRINT"{rvrs on}{down}({blue}THAT MEANS {red}GAME OVER{black})" 265 GOTO450
The first line reads musical note and duration values from DATA statements, then makes each note sound for that amount of time.
The screen is then cleared, and a game over message is displays.
It then goes to 450 to see if the player wants to play again.
270 DATA202,250,202,250,202,150,202,350,210,250,208,150,208,200,202,200,202,250,200,200 275 DATA 202,400
Those DATA statements contain the note value and length of the tune that plays when you die (funeral march). I did not know anything about music at the time, so I figured it out by ear. The timing is awful.
Time exceeded
285 PRINT"{down:2}{rvrs on}PRESS ANY KEY TO START" 300 PRINT"{clear}{black}{rvrs on}{down:3}WELL WHAT DO YOU KNOW?" 305 PRINT"{rvrs on}{down:2}WHILE YOU WERE DOWN ONTHE GROUND JUMPING A- ROUND,YOUR TIME CLOCK" 310 PRINT"{rvrs on} JUST RAN OUT!" 315 PRINT"{rvrs on}{down:2} NEXT TIME {red}WATCH{black} THE CLOCK!" 320 GOTO450
This message is displayed when time runs out. It then goes to the “play again?” screen.
Screen completed
400 S=S+1:GOTO15
This increments the screen, and then goes back to display it.
This prompts the user to ask if they want to play again. It waits for a key press, then if it is “Y”es, it goes to 480 (start a new game). If it is “N”o, it goes to 550 (end the game).
Press any key to play again prompt
480 PRINT"{clear}{black}{rvrs on}{down:2}AT LAST! SOMEONE BRAVE ENOUGH TO {red}TRY{black} TO STOP KING KING'S COUSIN!" 490 GETA$:IFA$=""THEN490 495 RUN
If the player chooses “Y”es to play again, this messages is displayed. It waits for any key to be pressed, then re-runs the program.
This cleans the screen, draws the ape, and then makes it “fall” to the bottom of the screen. Remember how Donkey Kong would fall at the end of the rivets level? Kinda like that. Just not as good. I had forgotten about this!
That plays a sound effect when the ape hits the bottom of the screen.
Game won message
535 PRINT"{clear}{black}{rvrs on}{down:3}CONGRATULATIONS!!!!!!!{down} YOU NOW KNOW WHAT IT FEELS LIKE TO BE ON" 540 PRINT"{rvrs on}TOP!{$a0}YOU PLAYED WELL!" 545 PRINT"{rvrs on}{down:2}{blue}BET YOU DON'T WIN NEXT TIME!" 548 PRINT:GOTO450
This displays the “game won” message, then goes to 450 to ask the player if they wish to play again.
Quit game message
550 PRINT"{clear}{black}{rvrs on}I DIDN'T THINK YOU HAD ENOUGH COURAGE TO TRY TO STOP A MAD APE!" 560 PRINT"{down}{rvrs on}{red}I HOPE YOU ENJOYED NOT SAVING THE BUILDING!" 565 FORG=1TO10000:NEXTG 570 SYS64802
This would display a message, wait, and then reboot the VIC-20.
Programmer’s thoughts
I actually expected this code to be far worse than it actually is. If I were writing it today, I see a number of things I could do to make it more efficient. I also have many ideas of ways I could use the VIC-20 features to make the game fancier (animated characters, better music and sounds).
But, overall, I am fairly pleased with how my junior high self programmed this. I was completely self-taught from reading the VIC-20 manual and articles in magazines.
But since I know I could do it better today, I think I might just give that a shot.
Here is the full listing of this version of the program. If you want to run it, I would do two things:
Delete line 1 entirely.
In line 10, remove the POKE772,200.
And I’d get rid of the same POKEs in the INSTRUCTIONS loader.
And if you don’t want to type it in, here is a virtual tape for the VICE Commodore emulator. It has the above changes made, but otherwise is exactly as I last touched it in 1983. Enjoy!
It seems like all I’m doing lately is regurgitating things that Robin of 8-Bit Show and Tell has already done.
So let’s do that again.
Don’t blame me. Blame YouTube!
YouTube did what YouTube does and it showed me another of Robin’s well-done videos. This one caught my attention because it dealt with the Commodore VIC-20 and its Super Expander cartridge.
The main thing that pulled me away from Commodore was seeing the TRS-80 Color Computer’s Extended Color BASIC. The CoCo had simple commands to play music and draw lines, boxes and circles. It also had this wondrous ELSE command I’d only heard rumors about.
On the VIC-20, it seemed you needed to use POKE and PEEK for just about anything graphics or sound related. Thus I gave up a computer with programmable characters and a hardware sound chip for a machine that had neither. On my new CoCo, at least I could draw a circle and play music without needing pages of DATA statements and cryptic PEEKS and POKEs.
Commodore was aware of this shortcoming, and they sold the Super Expander as a way to make up for it. Not only did it provide an extra 3K of memory (giving a whopping 6.5K for BASIC), it also added new commands to do “high resolution” graphics including drawing lines and circles, as well as ways to PRINT music using simple notation.
I used the Super Expander to do TV titles for fishing videos my father shot and edited. It was a thrill to see my VIC-20 graphics on TV screens at the Houston Boat Show.
But no one else could run my programs unless they had purchased the Super Expander as well.
But I digress.
(And besides, the Commodore 64 was $600 when it came out, and I was able to get a 64K CoCo 1 for $300 at the time.)
Don’t blame YouTube. Blame Twitter.
Robin’s video was making use of the Super Expander to let the VIC-20 solve a challenge initiated by Twitter user Dataram_57. On June 8th, 2019, they wrote:
I challenge every mathematician and programmer to solve this problem. Write the equation that you will use to change the position of the red point according to time and the animation. (You can use every function without if statement)???? #programming#math#mathgames#programmerpic.twitter.com/UDgGv1pajB
— D̨̯̞̜͇a̶͙̤t̵̪̤̺̪ͅḁ̱̳̱͠r̡a̦͞m͍̩̗͉͢ (@Dataram_57) June 8, 2019
This was, more or less, a classic bouncing ball program very much like the ones I have been writing about lately. But, all mine certainly made use of IF. Here’s how mine started:
0 REM bounce.bas
10 CLS:X=0:Y=0:XM=1:YM=1
20 PRINT@Y*32+X,"O";
30 X=X+XM:IF X<1 OR X>30 THEN XM=-XM
40 Y=Y+YM:IF Y<1 OR Y>13 THEN YM=-YM
50 GOTO 20
That’s not a very good example. It doesn’t erase itself, nor does it use the bottom line to avoid screen scrolling when the ball hits the bottom right position. It does show how I would use X and Y coordinates then an XM (X movement) and YM (Y movement) variable to increment or decrement them based on if they hit an edge.
The parameters of Dataram_57’s challenge were as follows:
Width: 90
Height: 80
Starting Position: 0,0
Time: ???
I wrote a quick graphical program do do this using my X/Y/XM/YM method:
0 REM dataram_57 twitter challenge
1 POKE 65495,0
10 W=89:H=79:X=0:Y=0:T=0:XM=1:YM=1
20 PMODE0,1:PCLS0:SCREEN1,1
30 LINE(0,0)-(89,79),PSET,BF
40 PSET(X,Y,0)
50 X=X+XM:IF X<0 THEN XM=1:GOTO 50 ELSE IF X>=W THEN XM=-1:GOTO 50
60 Y=Y+YM:IF Y<0 THEN YM=1:GOTO 60 ELSE IF Y>=H THEN YM=-1:GOTO 60
70 T=T+1:IF T=7031 THEN END
80 GOTO 40
The first thing to notice is that I draw a filled box from 0,0 to 89,79 and then set black pixels in it. This lets me visually verify my line is going all the way to the edge of the 90×80 target area. Also, I am using the CoCo 1/2 double speed poke since this is time consuming. If you do this on a CoCo 3, feel free to use POKE 65497,0 instead.
Twitter user Dataram_57’s challenge running on a CoCo.
Eventually the area should be entirely black when every dot has been erased.
How long has this been going on?
I did some tests and figured out that it takes 7032 iterations (0-7031) for the dot to cycle through the entire 90×80 area before it has erased all the other dots.
With that in mind, I propose we turn this into both a logic and optimization challenge. On the CoCo, let’s see if we can use the PMODE 0 screen (128×96 resolution with 1 color). We can put this in a modified version of benchmark framework for 7032 cycles and see how fast we can do it. (By modified, I am removing the outer “try this three times and average the results” loop.)
My example, using IFs, looks like this:
0 REM dataram_57 twitter challenge 2
1 POKE 65495,0
5 DIM TM,A
10 TIMER=0:TM=TIMER
15 W=89:H=79:X=0:Y=0:XM=1:YM=1
16 PMODE0,1:PCLS0:SCREEN1,1
17 LINE(0,0)-(89,79),PSET,BF
20 FORA=0TO7030
30 PSET(X,Y,0)
40 X=X+XM:IF X<0 THEN XM=1:GOTO 40 ELSE IF X>=W THEN XM=-1:GOTO 40
50 Y=Y+YM:IF Y<0 THEN YM=1:GOTO 50 ELSE IF Y>=H THEN YM=-1:GOTO 50
80 NEXT
90 PRINTTIMER:END
Mine, running in Xroar, displays 9808 at the end. And it’s not the correct way to meet the requirements of the challenge, so … the real versions may be faster, or slower.
Your challenge, should you decide to accept it…
Our challenge is to:
Rewrite this to work WITHOUT using any “IFs”.
Try to make it as fast as possible while keeping the benchmark code (lines 5, 10, 20, 80 and 90) intact. You can add variables to the DIM, but otherwise leave those lines alone.
What says you?
Credit where credit is due…
And lastly, for those who want to cheat, here is the solution that Robin came up with using the VIC-20 Super Expander cartridge…
Is his the only way? The best way? The fastest way?
This document was originally written in 1987 and was available on my BBS. It was originally in some kind of text editor format that I do not remember, so I have reformatted it for WordPress and made it available here for archival purposes. If you want the original file, here it is:
A “Hopefully Helpful” Text file by Allen C. Huffman
The 6809 CPU can only access 64K at a time. In order for an 8-bit CPU to access memory beyond that range, some sort of Memory Management Unit must be used. The CoCo 3’s MMU does the job nicely by breaking all of the memory into 8K chunks. The CoCo 3 is set up to handle up to 512K of memory which is addressed from &H0000 to &H7FFFF (0 – 524287). The 6809 will recognize the last 64K in this map (&H70000 to &H7FFFF) as “normal” memory for it. (In Basic, when you do a POKE 1024,42 it is actually putting a 42 in physical memory &H70000 + 1024. (If you do an LPOKE 1024,42 you will actually be putting the 42 in physical memory location 1024.) This may seem strange at this time, but hopefully it will soon be clear.
In a 128K Color Computer, the RAM is located from &H60000 to &H7FFFF. (All memory from &H00000 to &H5FFFF is unusable unless you have 512K in which case your extra memory resides there.) The 128K is broken into two parts. One is the normal 64K workspace that the CoCo uses. The other 64K is used for Hi-Res graphics, screens, etc. (For more detail on where everything is, refer to your CoCo 3 owners manual Memory Map on Page 311.)
Meanwhile, back to the MMU, all of the CoCo’s memory is handled internally as 8K blocks. Thus, a 64K workspace is actually 8 MMU blocks. (8 x 8K is 64K, right?) The MMU can access any of these 8K blocks by using the corresponding block number. The blocks are numbered as follows:
That covers the 512K area. On a 128K CoCo, you can’t even use blocks 00 to 2F, so let’s look at what you can use:
30 60000 - 61FFF 31 62000 - 63FFF The first 4 blocks (32K) is where Basic puts the 32 64000 - 65FFF HSCREEN graphics. 33 66000 - 67FFF
34 68000 - 69FFF This is where the HGET/HPUT buffer is. (8K)
35 6A000 - 6BFFF This is the secondary stack area. (8K)
36 6C000 - 6DFFF This is where the 40/80 column screen goes. (8K)
37 6E000 - 6FFFF And this one is unused by Basic. (8K)
That takes care of the 64K you don’t have direct access to on startup. Here is the 64K you do have access to:
38 70000 - 71FFF The first 32K is normal system RAM. (System use, 39 72000 - 73FFF 32 column text screen. PMODE graphics, Basic program 3A 74000 - 75FFF and variable storage) 3B 76000 - 77FFF
3C 78000 - 79FFF The last 32K is where system ROM goes. (Note that 3D 7A000 - 7BFFF on the CoCo 3, all ROM is copied into RAM so these 3E 7C000 - 7DFFF locations are indeed RAM.) 3F 7E000 - 7FFFF
(Notice that all you have to do to convert physical locations to normal 8 bit locations on the older CoCo’s is to remove the 7.)
So now you should have some idea of how memory is looked at, but how do you actually use it? Eight memory locations control what physical block of memory goes where. Let’s have another chart…
FFA0 0000 - 1FFF Remember those block numbers given earlier? FFA1 2000 - 3FFF On startup, FFA0 - FFA7 contain: FFA2 4000 - 5FFF FFA3 6000 - 7FFF 78, 79, 7A, 7B, 7C, 7D, 7E, & 7F. FFA4 8000 - 9FFF FFA5 A000 - BFFF WHY? Shouldn't they be 38, 39, 3A, etc, as the FFA6 C000 - DFFF block numbers would indicate? Read on... FFA7 E000 - FFFF
The MMU locations have bit 6 set high which adds &H20 (64) to their actual value. Therefore, to the MMU 38 is the same as 78, 3E is the same as 7E, etc. The CoCo 3 Service Manual indicates that programmers may use any blocks in the range of 00-3F so we will go by that. Please try not to let this confuse you! Just accept it.
In order to use the MMU from Basic, you simply use the POKE command to tell the MMU what “block” goes where. For instance, location &HFFA2 contains 7A on startup. Anytime you peek memory between 4000-5FFF it is actually PEEKing from block 3A which is physical locations 74000-75FFF. You can change this by POKEing to it. If you POKE &HFFA2,&H30 then anytime you PEEK or use memory in the range of 4000-5FFF it will actually be coming from physical memory 60000-61FFF. Whatever was in block 3A is still there, but when you try to use that memory, the MMU points the CPU to somewhere else. The CPU never knows the difference! To get it back as before, POKE &HFFA2,&H3A.
You may now have a slight understanding of how those Basic HSCREEN save/load routines work. Take a look:
10 INPUT "Save picture as";N$ 20 FOR A = &H30 TO &H33 30 POKE &HFFA2,A 40 SAVEM N$+"/HR"+CHR$(A-&H2F),&H4000,&H5FFF,0 50 NEXT A 60 POKE &HFFA2,&H3A
Line 10 simply asks for what to save the picture as. Line 20 does a loop. If you notice 30-33 is the physical location of the hi-res HSCREENs. The poke in line 30 switches the physical location pointed to by “A” into CPU location 4000-5FFF. (The first time through, block 30 is there, then 31, then 32, etc.) After this, line 30 does a SAVEM of whatever is now at that location. (It tags the extension of HR at the end followed by a number 1-4). Line 50 continues the loop and line 60 restores the memory like it was before this routine.
Programmers out there might think this is strange seeing a program save the same contents of memory (4000-5FFF) four times, but that is where the magic of the MMU comes in. Thanks to the POKE, each time the program gets to that line the MMU points to a different area. To load a file back in, a similar routine must be used that flips the MMU to that area then, after loading, flips it back where it goes. It might look something like this:
10 INPUT "Load picture";N$ 20 INPUT "HSCREEN #";H 30 HSCREEN H 40 FOR A = &H30 TO &H33 50 POKE &HFFA2,A 60 LOADM N$+"/HR"+CHR$(A-&H2F) 70 NEXT A 80 POKE &HFFA2,&H3A
If you can understand why this program works, you are in good shape. By theory, someone could save the entire memory of the Color Computer to disk in 8K chunks using the above method. (Why? I don’t know, but they could…)
Now if you still don’t see how this works, try this experiment. Enter the following line which will fill up physical block &H37 (6E000-6FFFF) with the value of 42.
FOR A=&H6E000 TO &H6FFFF:LPOKE A,42:NEXT A
Okay. So all the memory from 6E000 to 6FFFF should have 42s in it, right? Right. (To verify, you could do FOR A=&H6E000 TO &H6FFFF:PRINT LPEEK(A):NEXT A and you would get 42s back.)
Somewhere in memory we have a block of 42s. We can make that block appear to be somewhere else. Try a POKE &HFF2A,&H30. You have just set it so whenever the CPU looks at locations 4000-5FFF it will really be looking at 60000-61FFF. Confirm this and try:
FOR A=&H4000 TO &H5FFF:PRINT PEEK(A);:NEXT A
You will see a bunch of 42s. Magic, right? No, just amazing. Still not convinced? Type POKE&HFF2A,&H3A and try that previous line again. What happened to the 42s? They are still where they were originally but, the CPU is being directed back to where it was supposed to be.
This is just the start of what the MMU can do. There is another bank of MMU registers from FFA8 to FFAF. These are being used by the CoCo itself while in basic, but any machine language programmer could define each set of MMU registers and then by toggling a single bit (bit 0 of FF91) he could select between which MMU map would be used.
If you still haven’t got it, leave a message for me and I will do my best to answer questions or to create another file that further explains the MMU.
In honor of the 40th anniversary of the Radio Shack Color Computer, I am making my book, CoCoFest Chronicles, available as a free download. This book was first published in 1998 and has been out-of-print for many years.
The book contains updated versions of my CoCoFest reports covering events from 1990 to 1997. It also contains behind-the-scenes tidbits that explain various inside jokes and things that went on that were not included in the original reports I uploaded to online services back then.
Thanks to Rob Inman for sharing this link over on Discord. This was supposed to have been posted in July 2019, but I just found it in my drafts folder. I think I was going to write an article about it, but forgot.
Someone in the UK is selling an all-in-one RS-232 to WiFi adapter. They use Bo Zimmerman’s excellent Zimodem firmware, though the version they use is based on my fork of the project with the defaults set to standard RS-232 rather than Commodore’s inverted RS-232.