I have made it back from the 2017 26th annual “Last” Chicago CoCoFEST! It was a pretty spectacular year with many new hardware items being demonstrated, as well as new software projects. I will try to share some details and photos when I get caught up.
In 1985, a new game was advertised by Prickly-Pear Software for the Radio Shack TRS-80 Color Computer:
Here is the ad found on page 68 of the the December 1985 issue, typos and all:
As a kid who had watched Doctor Who on the local PBS station in Houston, Texas, I just had to have this game. I had just moved from Houston to the tiny town of Broaddus (population 225) and no longer could receive a PBS station, so this would be as much Doctor Who as I would get while living there.
I do not think I even had a disk drive of my own yet, since I recall ordering this on cassette.
In those days, us kids would go down to the post office and buy a money order and mail it to the company, then wait for them to mail back the software. This really helped build up anticipation.
Finally, the game arrived:
It came with a manual, which I have yet to locate. When I find it, I will scan it and upload it to the Color Computer Archive. Fortunately, it at least has some instructions:
And, the promise of many levels of difficulty:
I was a bit disappointed to find that the game was written in BASIC. It also included one machine language program called “CTRYROAD.BIN.” This was a multi-voice music file that played John Denver’s Country Roads hit from the 1970s. And it played it while displaying the instructions, so you had to sit there for several minutes until the song was complete.
After this, the game would present some side scrolling text explaining what keys did what. Thank goodness for the manual! The text went by pretty quick.
After several minutes of generating data for the game, you were presented with the main game screen:
And, you also had the map view:
There were also animated scenes that showed the TARDIS descending to the planet’s surface, but without the manual, I cannot remember how to even get to the planets.
But … the effect was less than impressive. It was a very disappointing purchase from a cosmetic and user-interface standpoint.
HOWEVER, the actual game was quite good. It reminded me of the Star Trek-style games where you moved around a galactic grid in search of starbases and Klingons.
I must have written Prickly-Pear Software because somehow I got in touch with the author, Larry Lansberry, and we exchanged some letters. I recently found some of these letters, so I need to re-read them and see what we discussed. I know he explained the music choice and why the animation style was done like it was.
I always thought this game would be fantastic with a facelift. Perhaps now that it has been unearthed, someone may consider doing an update/remake of it.
You can try it yourself in a CoCo 1/2 emulator. The disk image is available here:
And, just for fun, an old Doctor Who music demo I wrote has also been posted:
Side Note: I don’t think Zigwald X. Malushi was a real person, and if he was, he had nothing to do with this demo. I used to see that name pop up on random pieces of software, often of the “questionable” nature. Since I was posting a copyrighted tune, I did not want to post it under my name so I used that one. I should find some of the other “Zigwald” programs and make a collection of them. I wonder how many used that name.
NOTE: This is a work-in-progress. Check back for updates. I will note any revisions at the top of this article.
- 2017-03-19 – Fixed a typo (thanks, Roger), and minor clean up.
Cyclone CoCo – Roger Taylor’s FPGA CoCo on a Chip
In July 2015, Roger Taylor began the process of recreating a CoCo 3 system in an FPGA (field programmable gate array). Using a low-cost (less than $80) Terasic DE0-Nano development board and a custom I/O add-on board, he quickly got a virtual CoCo running. He has been continually improving the system and adding more features. His “CoCo on a Chip” Facebook group has documented this every step of the way.
Last year, I had acquired one of these DE0-Nano development boards and the RGB22VGA add-on board from Ed Snider. I was planning on using it to hook my old CoCo 3 up to a borrowed VGA monitor. But, at last year’s 25th annual CoCoFEST! near Chicago, Mark Marlette of Cloud-9 was selling his all-in-one version of this RGB to VGA adapter. I picked on of those up, and never used the DE0-Nano.
Roger recently sent me one of his add-on boards so I can finally put together my own FPGA CoCo. Let’s see how it goes…
- Terasic DE0-Nano Development and Education Board. These list for $79 on the manufacturer’s website ($61 for academic purchasers). It comes with a retractable USB Mini cable, which will be used to program the FPGA.
- Roger Taylor’s Terasic DE0-Nano Upgrade Daughter Board. He sometimes has these available completely assembled for around $99 (via his e-Bay store). You can also just buy the bare boards and build one yourself.
- SD memory card. Currently, the project does not support SDHC cards, so you have to find an older, smaller capacity SD (SDSC) card like a 2GB. I picked up a Transcend 2GB card on Amazon for about $7.
- PS/2 Keyboard (and optional mouse). I have never owned anything that used PS/2, so I didn’t have an old keyboard to use. I found a cheap Logitech Classic Keyboard K100 on Amazon for $11.99.
- VGA Monitor. I have one I borrowed to use with my CoCo, but I think VGA ports can still be found on some modern monitors.
- USB Power Supply for the DE0-Nano. You can use the included USB Mini cable and use a USB power port, or use a similar USB charging cable with a Mini connector.
About Roger’s Terasic DE0-Nano Upgrade Daughter Board
Roger’s add-on board has RAM, two PS/2 style ports, a 1/8″ audio output jack, and VGA port. There are header connectors for plugging in a module that handles the SD card, and ones for WiFi and Bluetooth. My unit has The SD card and WiFi installed. I will be ordering a Bluetooth module for wireless serial ports (they are very cheap).
The card has writing along the bottom indicating which direction it should be plugged up tot he DE0-Nano: “Faces DE0-Nano USB connector –>>”:
You can also tell orientation by the cutout in the board, which lines up with some small 2-pin connector on the DE0-Nano below:
- Roger recommends installing the Quartus II programming app. (Smaller than having to install the full Quartus II IDE).
- You will also need to install the USB Blaster device driver, which will be found in the install directory of the programming app.
- You also want the latest Cyclone CoCo firmware from his Facebook group. He calls it Cyclone CoCo, for reasons that will become obvious once you start using it :)
It seems the first step will be to install the Terasic software, and then hook the DE0-Nano up and load Roger’s firmware. The software is only available for Windows and Linux. Since I am a Mac user, I will either have to run it via virtualization (I use Parallels Desktop and Windows 10) or find a PC to use. I have several Raspberry Pis running Linux, so maybe that is an option as well.
After that, you will have to install a driver, and lastly you will be able to load Roger’s firmware on to the device and turn it in to an FPGA CoCo. Here are the steps I took:
Step 1: Install the free programming app.
Download it here:
I am using the Windows version, so my screen shots will be from that version.
This will make available a program called “Quartus II 12.1 Programmer” in the Altera 12.1 Build 177 folder.
NOTE: Your version number may be different if you are using later or earlier versions than I have. If you are setting up one of these, I’m sure you can figure that out.
Step 2: Install the USB driver.
When you plug in the DE0-Nano to the computer, Windows should prompt you for a driver. My Windows 10 did not, so I had to manually install it. If yours does, you can skip the next step and go straight to how to browse to the driver.
First, I had to open the Device Manager and find the USB-Blaster in the Other devices section:
Double clicking on USB-Blaster brings up information about the device:
Select “Update Driver…” and then choose “Browse my computer for driver software“. You have to manually tell it where the driver will be found (inside the programmer install directory):
On my system, I used the default install location and the driver was located here:
Your directory names may be different, depending on what version of the software you install. Browser there, and then click Next:
The system should find the driver and install it.
Step 3: Program the DE0-Nano
Plug in the DE0-Nano to a USB port using the included cable, then launch the Quartus II 12.1 Programmer application.
If your Hardware Setup box says “No Hardware”, click the Hardware Setup button and select “USB-Blaster“:
After you close that windows, you can use File->Open to browse to and load the FPGA firmware image you downloaded from Roger’s Facebook page:
When it is loaded, you will need to check off “Program/Configure” and (probably optional) “Verify” to enable the Start button:
Once those two buttons are checked, you can then click Start to begin programming. After a few moments, you should see “100% (Successful)” in the top right green box:
Congratulations! You now have an FPGA CoCo!
Hello, FPGA CoCo!
After these steps, I plugged the device up to a VGA monitor and plugged in my PS/2 keyboard to the left port, and powered it up. It should instantly come to life with a familiar green screen, but if you have not prepared the special SD card for it yet, it will hang, trying to boot from that card.
You can hold down ESCape when you power up to bypass that, and find yourself at a nice virtual CoCo:
In the next part, I will figure out how to configure the SD card so I can actually load some software on it, and save any programs I create.
Until then … wow. This was easy! I can’t wait to see what all it can do. Thanks, Roger!
Now working without the crystal. This is using a Teensy and one of the pins to generate a 4mhz signal. I will post a video in coming days, hopefully, as soon as I have some CoCo BASIC code talking to it.
The green and black wires running off of it wold go to a headphone jack that the cassette cable sold plug in to so you could feed Sound back to the CoCo without needing to use an external speaker. AUDIO ON!
NEW “PRODUCT” ANNOUNCEMENT
The team that brought you* the CoCoPilot DriveWire Server is proud to announce their latest innovation:
Sir Sound is a solid-state multi-voice audio synthesizer that operates over a serial transport mechanism**. It provides arcade-quality*** sound with up to three independent tonal voices plus one white noise channel all in an external module that doesn’t require voiding your warranty to install. In fact, you won’t even need tools!
Pricing is to be announced but hopefully it will be around $50. Or maybe $30. Or cheaper. Or less if you build it yourself. Heck, we’ll probably just make kit versions available since we don’t really like to solder.
Sir Sound Configurations
- Turnkey – This is a “plug and go” version where you just plug it in and go. No special drivers are needed, as they are already built in to both BASIC and OS-9.****
- BYOE – The bring-your-own-everything edition is shipped as a set of simple instructions containing a parts list and how to run wires between the parts.
- Custom – Also planned to be available are various custom configurations, like what color of case it comes in.
We estimate the thing is gonna cost us, like, ten or so bucks to make using off-the-shelf parts ordered in small quantities from China. But, to make it a product, we really should have an integrated circuit board and a case made, which will run the costs up dramatically. Rest assured, we’ll pass those unsavings along to you!
The first prototype is in the process of being tested. Quit rushing us. We’ll let you know when it’s done.
You kinda have to use some micro-controller since the sound chip turns on and starts making sound. Something has to issue the “shut up” instruction to it. If you just had hardware to translate a serial byte in to the command, and made the CoCo do all the work, the CoCo would have to load and run a program to shut the thing up every time you powered up. Fortunately, a custom-built Arduino that handles this can be done for like $5. There are cheaper PIC chips that could do it for less.
Then, you add a MAX232 type chip that goes from the TTL signal levels of the Arduino to RS232 signal levels, or using one of these $3 (or less) boards that’s already wired:
Lastly, add a CoCo serial cable (4-pin DIN to DB9), and you are set.
A small program on the Arduino will monitor the serial port for bytes and then relay them to the sound chip.
By doing some POKEs in BASIC to set the baud rate, you could make music by doing things like this:
REM PLAY MIDDLE C PRINT #-2,CHR$(&H8E);CHR$(&H1D);CHR$(&H90); FOR A=1 TO 1000:NEXT A REM VOLUME OFF PRINT #-2,CHR$(&H9F);
The notes always play, so you shut them off by setting volume off. There are different channel values for each of the four channels.
I envision having a “raw” mode where the device just translates the bytes from serial to the sound chip, and a “smart” mode where you could use an API and just send note values (like 1-88 of a piano keyboard, or MIDI note values).
“Smart” mode could simplify the control so it might look like this:
REM ALL DATA: PLAY CHANNEL 0, NOTE 10, AT VOLUME 15 PRINT #-2,CHR$(&H00);CHR$(&HA);CHR$(&HF); REM NOTE ONLY: PLAY CHANNEL 0, NOTE 10 PRINT #-2,CHR$(&H01);CHR$(&HA); REM NOTE ONLY: PLAY CHANNEL 1, NOTE 10 PRINT #-2,CHR$(&H11);CHR$(&HA); REM VOLUME ONLY: CHANNEL 0, VOLUME 5 PRINT #-2,CHR$(&H20);CHR$(&H5);
And, I could also add a “super smart” mode where it could parse PLAY command-style strings, then spool them in the background while you do other things:
REM PLAY COMMAND, CHANNEL 0 PRINT #-2,CHR$(&H30);"CDEFGAB";
And, a “super super smart” mode could let it store string sequences, and play them by triggering with a simple byte:
REM STORE NOTE SEQUENCE 0 PRINT #-2,CHR$(&H40);"CCDCECFECCDCECFE";CHR$(0); REM PLAY NOTE SEQUENCE 0 PRINT #-2,CHR$(&H50); REM PLAY NOTE SEQUENCE 0 FIVE TIMES PRINT #-2,CHR$(&H55);
…or whatever. You could sequence them together, like MIDI sequencers do, and have complex patterns that could play in the background while the program does other things.
There are lots of possibilities. We could even see about using the Carrier Detect line as a way to tell if the sound card was still playing something (rather than needing routines to read data back from the device, which would be doable but not from BASIC without assembly language code).
If this “sounds” fun to you, leave a comment…
* If you call making a blog post “bringing it” to you.
** It plugs in to the Serial I/O port. “Sir” sounds like “Ser”, get it? Marketing thought SerSound wasn’t friendly enough.
*** This part is true. The same sound hardware is used in the arcade mega-hits Congo Bongo and Mr. Do, among others.
**** PRINT#-2, yo.
Well, here we go again with a few more tidbits from comments to a previous installment…
Lowercase to Uppercase
John Klassek once again provides some interesting tips. He looked at my Building a Better BASIC Input article example:
30 LN=INSTR(“YyNn”,A$):IF LN=0 THEN PRINT”YES OR NO ONLY!”:GOTO 20 40 ON LN GOTO 100,100,200,200
…and suggested this:
40 ON (LN+1)/2 GOTO 100,200
…and you don’t have to keep the line numbers twice in ON…GOTO.
Well that makes sense. The extra bytes taken to do the math would be smaller than all the doubled line numbers. But does doing the math take more time than parsing through the extra line numbers? Let’s fine out:
5 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 LN=INSTR("YyNn",A$):IF LN=0 THEN PRINT"YES OR NO ONLY!":GOTO 20 40 ON LN GOTO 100,100,200,200 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END 100 GOTO 70 200 GOTO 70
This produces 866.
5 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 LN=INSTR("YyNn",A$):IF LN=0 THEN PRINT"YES OR NO ONLY!":GOTO 20 40 ON (LN+1)/2 GOTO 100,200 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END 100 GOTO 70 200 GOTO 70
…jumps up to 1214! So, smaller code, but at a speed penalty. (See: Size versus Speed.) Brute force is often faster.
However, I also discussed converting characters to UPPERCASE so you only had to compare uppercase. That came with quite the speed penalty:
0 DIM A$,C:A$="*" 5 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 40 C=ASC(A$):IF C>=97 AND C<=122 THEN A$=CHR$(C-32) 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END
That benchmark was 960.
John then presented a much faster approach by using strings and the INSTR command:
0 DIM A$,C,Z:A$="*" 1 AL$="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" 5 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 40 Z=INSTR(AL$,A$): IF Z>1 THEN C=64+Z/2 ELSE C=ASC(A$) 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END
That drops it a bit lower to 920. For a custom input routine, every bit helps make it keep up with faster typing. Nicely done! (He also notes that this assumes the character string is non-empty, which we’d ensure is the case if using an INKEY$.) And, remember that we could speed that up even more by changing the decimal numbers to HEX.
He also suggests using AND instead of my “C-32” subtraction:
In case of a conversion with A$=CHR$(ASC(A$)-32) this could be a slightly faster: A$=CHR$(ASC(A$)AND M) but only with a predefinition of M=223 (with constant 223 it would be slower!) which masks bit 5 (value 32) in the character code representation.
0 DIM A$,C,M:A$="*" 1 M=&HDF '110111115 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 40 C=ASC(A$):IF C>=97 AND C<=122 THEN A$=CHR$(C AND M) 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END
This returns 963 while my original using subtraction returned 960. It looks like AND is slower. HOWEVER, if you know the input is already ONLY “a-z”, you can just do the AND without the check. So, there is a use-case if you were scanning for “yYnN” and just AND the result and compare only uppercase….
It’s fun to think outside the box.
Any other suggestions?
You may have never heard of the Texas Instruments SN76489, but if you are reading this article, there’s a good chance you have heard it.
The SN76489 is a sound chip which, according to the Wikipedia entry, was used in systems such as:
- ColecoVision (and it’s home computer counterpart, the Adam)
- Sega Game Gear portable (a clone chip)
- Sega Master System (a clone chip)
- Tandy 1000 (and the IBM PC jr?)
- Texas Instruments TI-99/4A (earlier variation)
…and in arcade games such as:
…and many more. I am just naming the machines and games I have heard of or seen/played.
Side Note: The Wikipedia entry also claims the Sega Genesis used one, but it had far fancier sound. A quick search shows the Genesis did not use this chip, so other systems may also be incorrect. Ah, Wikipedia…)
This chip is able to produce three tones and one white noise at a time, which sounds an awful lot like the audio capabilities of my first computer, the VIC-20.
The chip has none of the fancy synthesizer features found in other chips, such as the famous Commodore 64 SID chip. The only thing you can do is adjust the volume level of each channel of sound. Clever software uses this to produce bell sounds and other effects. (If Congo Bongo is really using this chip, it’s doing some fancy things to make those bongo sounds!)
Thanks to StrongWare‘s John Strong, I now have one of these chips to experiment with. It is wired up to an Arduino Nano clone. (NOTE: I had issues getting this clone recognized on my Mac, due to it using a different serial chip. I found the solution, and wrote about it earlier this week.)
John pointed me to this short tutorial on how to use the chip:
Using sample code there, I was able to get the device making tones, and then expanded it to play a sequence of tones to make a tune.
The next day I added more code so it could do a multitrack sequence.
I thought it might be fun to share what I have learned the first two days of playing with the device, and share the code I have come up with.
I will do a full article on the chip and how it works (summarizing some overly complex explanations I have been reading), but until then, here is my sample project:
It contains routines to poke bytes to the SN76489, plus a work-in-progress multitrack music sequence that currently plays the 2-voice intro music to Pac-Man :)
I’ve been fixing up the comments and squashing some bugs, so check back for the latest. I still have to add real/better support for the “noise” channel, but it works right now for playing simple tunes.
More to come…
John Strong of StrongWare sent me an Arduino Nano to experiment with, and I was puzzled when it would not appear as a Port in the Arduino IDE. All I could select was “dev/cu.Bluetooth-Incoming-Port”.
I never had to install special drivers for Arduino on Mac before (but do on a PC) but I did some searching and decided to try the FTDI drivers (most recent release from 2015). No luck.
I then decided to try my UNO. I couldn’t find it, and had to open a brand new one up. It was also not recognized.
After some searching, I found out that many clone Arduino are using a different USB-to-Serial chip, and need special drivers. The “official” ones (from a Chinese website!) cause a kernel panic due to a bug in the current release.
I found this:
These drivers work and I now can use this clone Arduino Nano:
I hope this helps someone else not waste an hour like I had to tonight to find this ;-)
See also: Part 1
I had no intention of turning this in to another rambling multi-part article series, so maybe I can wrap this up in just two posts…
But first, in a comment to the first part, William “Lost Wizard” Astle said:
String manipulation is, in fact, entirely predictable, and logical. I think I’ll do an article on the internals of string evaluation and manipulation in Color Basic. It’s actually fairly straight forward, but the whys and hows of it get a bit complex. – William Astle
His article explains how the string handling really works (unlike my random speculation), and includes some interesting tips about how to make things faster by allocating extra string space if memory allows. Check it out here:
…and then read the rest of my speculative fiction about how strings work.
Speculative Fiction About How Strings Work
My original purpose was to talk about making sure you CLEAR enough string memory to keep your program from crashing if a user types in a big string or maxes out all the variables. This will become important when I get to designing the new version of the *ALLRAM* BBS I plan to write.
Today, let’s look at why this mattered when I created the original version back in 1983.
When I first created *ALLRAM*, I decided I would make the message board work on 64-character lines. That would fit nicely on two lines of the CoCo’s 32-column screen, or one line on the 64-column TRS-80 Model I and III screens. Isn’t it weird thinking back to the early days before 80 columns became standard?
On a cassette based CoCo, after a PCLEAR 0 (no graphics pages), I could get 31015 bytes of memory to use for storing the userlog and message base.
Somehow, I decided that I would let each message be up to 10 lines long (640 bytes max), and I used an extra line to store the TO, FROM and SUBJECT of the message. From looking at the old source code, I see that usernames were limited to 20 characters, and the subject was 18 for some reason. They were combined using backslash delimiters like this:
At max size (20+1+20+1+18) that is 60 bytes. Ah, now I understand why the subject must have been 18. This makes each message take up to 700 bytes total (640+60).
I used a two-dimensional array to hold the 11 lines for each of the 20 messages:
Twenty messages at 700 bytes each is 14000 bytes. I had to make sure I CLEARed that much plus anything else needed for operation.
I also decided I would store the userlog as an array of strings that contained the username, password and access level, separated by a delimiter:
A username could be up to 20 characters long and a password 8 characters. The level was supposed to be from 0-9, so it could be 1 character. Thus, the maximum userlog string could be 31 bytes (20+1+8+1+1).
I decided I would allow up to 201 users. I suppose I expected user 0 to be the SysOp (system operator) account.
This meant the userlog could take up to 6231 bytes (201*31).
Now the RAM storage used by the BBS was 20231 bytes, assuming every byte of every user and message entry was full.
I knew I would need some extra storage for temporary strings and such, so I just rounded up:
And that’s how *ALLRAM* came to exist on a 32K Radio Shack Color Computer. (I had 64K, but Color BASIC did not make use of anything past 32K, and I didn’t know assembly language yet which would have been needed to access the upper 64K. See this article for details.)
You may be able to see that this was a “safe” approach, but probably had a TON of memory that just wasn’t being used. Consider a message like this:
---------------------------------------------------------------- TO: ALLEN HUFFMAN FR: SYSOP SB: COOL! THIS SIMULATES WHAT A MESSAGE MIGHT HAVE LOOKED LIKE ON THE 1983 *ALLRAM* BBS, USING UP TO TEN TEXT LINES OF 64 CHARACTERS. IF I DIDN'T USE IT ALL, THE REMAINING MEMORY WAS JUST WASTED. -<>- ALLEN HUFFMAN -<>- ----------------------------------------------------------------
The array that contained this message would look like this:
---------------------------------------------------------------- MS$(0,0) = "ALLEN HUFFMAN\SYSOP\COOL!" MS$(0,1) = "THIS SIMULATES WHAT A MESSAGE MIGHT HAVE LOOKED LIKE ON THE 1983" MS$(0,2) = "*ALLRAM* BBS, USING UP TO TEN TEXT LINES OF 64 CHARACTERS. IF I" MS$(0,3) = "DIDN'T USE IT ALL, THE REMAINING MEMORY WAS JUST WASTED." MS$(0,4) = "" MS$(0,5) = "-<>- ALLEN HUFFMAN -<>-" MS$(0,6) = "" MS$(0,7) = "" MS$(0,8) = "" MS$(0,9) = "" MS$(0,10)= ""
The row of dashes (“—“) represents the 64 character line size. As you can see, this small message is wasting much space because of unused lines, and even leftover space on the lines that are used.
If I were going to redesign this today (which I am), I have several ideas that would probably make it far more efficient, allowing for much larger message bases using the same memory (or, worst case, the same size if every message was maxed out).
I’ll share mine in a future installment.
Many aspects of Microsoft Color BASIC have always been magic to me. I never gave too much thought to how the BASIC interpreter does what it does.
String variables are one thing we all had to know a bit about. By default, Color BASIC sets aside 200 bytes for strings. If you use more, or less, you learned about using the the CLEAR command:
CLEAR 1000 'RESERVE 100 BYTES FOR STRINGS
CLEAR erases all variables, so it has to be done before you define any:
10 A=1 20 PRINT A 30 CLEAR 40 PRINT A RUN 1 0
We can test CLEAR from BASIC.
If we reserve only ten bytes for strings:
…then we should be able to create a ten-character or less string:
But, if we try to create anything longer than ten characters, we get an Out of String Space error (?OS ERROR):
Any variables you create from the command line will be stored in string memory. But, strings can also be embedded in a BASIC program and not use any string memory:
10 CLEAR 10 20 A$="THIS STRING TAKES UP NO STRING MEMORY"
In the above example, A$ points to the text embedded inside the BASIC program. It can, therefore, be as long as a string can be since it takes up no string memory.
Consider this example… If you have 30 bytes reserved for strings, you should be able to make three 10-byte strings in memory:
10 CLEAR 30 20 A1$="1234567890" 'WORKS 30 A2$="1234567890" 'WORKS 40 A3$="1234567890" 'WORKS 50 A4$="X" '?OS ERROR!
If you run that, it works just fine because those strings are being stored in the program, and NOT in string memory. I caught myself not paying attention to my own article!
To force a string to live in string memory, we have to do something to force BASIC to move it there. If the string is NOT declared as a contiguous string of text, BASIC has no choice but to make it in string memory:
That is enough to do it. A1$ will be represented as “1234567890” somewhere in string memory.
And BASIC is not too smart. In this example, it looks like BASIC still COULD keep it in program memory (the string is contiguous), but it does not:
That will force the string in to string memory, and now the program will fail with an ?OS ERROR on line 40:
With that said, let’s see what we must do to avoid a program from crashing due to running out of string space…
First, we need to understand how much string memory the program needs and make sure we CLEAR that much. Suppose we wanted the user to enter a password of up to 10 characters. We could do this:
10 CLEAR 10 20 LINE INPUT "PASSWORD?";A$
If we run that, we can type up to 10 characters and press ENTER and it should work fine. BUT, if the user typed more than 10 characters, it will crash with an ?OS ERROR.
The INPUT and LINE INPUT commands actually lets you type up to 249 characters before it stops you, so if the programmer didn’t CLEAR extra string space, typing in a long line might be enough to crash the program.
As a programmer, if we plan to use INPUT, we need to have 249 bytes more than whatever string space we plan to use.
If we are going to be doing any string manipulation, we need to leave extra room for that, too.
Thus, if I was expecting the user to type in a 10 character password, and a 20 character username, and I know INPUT allows the user to type up to 249 characters, I guess I need this:
10 CLEAR 10+20+249
BUT, it will be important that the username and password strings are limited to just 10 and 20 characters. We can use LEFT$ to trim them down in case someone “accidentally” types too much:
10 CLEAR 10+20+249 20 LINE INPUT "USERNAME:";NM$:NM$=LEFT$(NM$,10) 30 LINE INPUT "PASSWORD:";PW$:PW$=LEFT$(PW$,20)
That code should now be bulletproof against a user typing in long strings at both those input prompts.
Side Note: The 249 value may be specific to the Microsoft Color BASIC on the Radio Shack Color Computer. Other versions of BASIC may have different input lengths and differences. I’d be curious to know what is similar or different on Atari BASIC, Commodore BASIC, etc.)
Basically, we must make sure our CLEAR command covers the maximum characters the variable is allowed to contain, plus any extra required by something like INPUT.
However, there are other things that use temporary string space we must also account for. Suppose we were going to combine the username and password along with a numeric access level (say, 0-255) in to a string, using some character to separate them:
This is basically what I did for my 1983 *ALLRAM* BBS for the userlog. Rather than using three different arrays – an array of usernames, an array of passwords, and an array of levels – wanted to just use one string array. This was because every variable (or array element) uses 5-7 bytes of memory (more details on this in a future article). The less variables, the less memory you use, so combining multiple elements in to one string saved me quite a bit.
My maximum size for a userlog “record” becomes:
- 20 bytes for the username
- 1 byte for the delimiter character
- 10 bytes for the password
- 1 byte for the delimiter character
- 3 bytes for the level number
A fully loaded userlong string would be:
12345678901234567890\1234567890\123 = 35 bytes
Therefore, if I was going to make such a record, I’d need 35 bytes for each record I allowed. For example, if I wanted five users, I’d need to reserve enough memory for five of those userlog strings, plus room to input a username and password, and the extra overhead for the INPUT buffer just in case the user tries to type too much:
10 CLEAR 5*35+20+10+249 20 DIM UL$(4) 'BASE 0, SO 0-4 30 FOR A=0 TO 4 40 PRINT "USERNAME";A;":"; 50 LINE INPUT NM$:NM$=LEFT$(NM$,20) 60 PRINT "PASSWORD";A;":"; 70 LINE INPUT PW$:PW$=LEFT$(PW$,10) 80 UL$(A)=NM$+"\"+PW$+"\0" 90 NEXT
If you run that, you can enter a username and password for each of the five users. There is enough memory reserved that even if every name uses the full 20 characters, and every password the full 10, it still shouldn’t crash from an Out of String Space error.
BUT … we got lucky. That extra 249 bytes we reserved for the INPUT buffer is actually keeping this from crashing on line 80. This is because line 80 has to create a new temporary string, so there has to be room to hold the original NM$ and PW$ plus room to create the new copies of those strings with the delimiter characters and the level at the end. Even though this string only exists for a moment, it still has to exist somewhere.
Let’s remove the 249, and ONLY TYPE the max characters (10 or 20):
10 CLEAR 5*35+20+10
Now if we run, we get a different result:
Even though we had enough memory to hold our temporary 20 character username (used by INPUT) and 10 character password (also used by INPUT), plus enough memory to hold the combined userlog strings, line 80 needed even more to create the new string.
Usually we just give CLEAR more memory and be done with it, which is fine for casual BASIC programming and if you have plenty of memory. But if you are needing every last byte of memory for a huge program or one with tons of variables and strings, maybe we can’t waste any.
I wondered how much this needed, so I wrote this test:
10 CLEAR 20+10+35 20 NM$="12345678901234567890"+"" 30 PW$="1234567890"+"" 40 UL$=NM$+"\"+PW$+"\255"
I used the +”” to force the strings to be placed in string memory.
It looks like this should work since the new UL$ will only contain the 20 character username plus 1 character delimiter plus 10 character password plus 1 character delimiter plus 3 character level…
But, during the process of building the UL$, it seems multiple temporary strings are created.
From trial and error, I found that adding 31 bytes was enough to make it work:
10 CLEAR 20+10+35+31
31 bytes doesn’t really fit anything we have. That’s the 20 character name plus delimiter plus password … but what about the rest? There’s still the delimiter plus three character level at the end: “\255”
Look at line 40 … see the “\255”? That’s where the extra four bytes are. It looks like BASIC doesn’t need to count that string since it’s already in the code. It still needed one byte for the first delimiter, though, so perhaps being at the end matters?
If I try this:
…then it works with 25 extra bytes. Er… It looks like it’s not that the last part was a four character string. It looks like it’s just the “last” element. In this case, the password is 10 characters, so that would get us back to our 35 character string size we reserved.
Let’s test this further:
Now it works with 15 extra bytes. That’s the password (10 characters) plus the bit in the middle (5 characters, “\255\”).
This tells me that whatever string manipulation is happening, BASIC only needs string memory for everything but the last manipulation. Thus, if I had something like this:
10 CLEAR 11+10 20 A$="12345"+"67890"+"X"
As I expected … I need enough reserve string space for it to build the first part of the string in a temporary location, before doing the final assignment of temporary + final bit.
10 CLEAR 10+9 20 A$="123"+"456"+"789"+"0"
Now that I work through it, it makes perfect sense. Above, BASIC is creating a temporary string and doing this:
- store “123” in temporary string (usage: 3 bytes)
- store “456” in temporary string (usage: 6 bytes)
- store “789” in temporary string (usage: 9 bytes)
- copy temporary string to A$ and append “0” (usage: 10 bytes)
This seems to be a good educated guess, but could be confirmed by consulting the source code.
I guess it looks like the amount of temporary string usage can be predicted just by looking at all the places in the code where string manipulation is done, and calculating which one is the largest and adding that much extra string space.
BUT, there are other rules for things like LEFT$, MID$, RIGHT$, HEX$, etc.
10 CLEAR 9+4 20 A$="12345"+HEX$(65535)
Above, that needs 4 extra bytes to create the HEX$ string (“FFFF”) and then append it.
10 CLEAR 10+5 20 A$="12345"+LEFT$("67890ABCDEF",5)
Above, we need 5 extra bytes for the temporary 5 byte string that LEFT$ is going to create and append.
10 CLEAR 10+5 20 A$="12345"+MID$("ABCDEFG",1,5)
Above, 5 extra bytes are needed because MID$ creates a 5 byte string to append.
Now it all makes sense.
Wow. That was one heck of a tangent.
I am going to say it’s probably save to just add an extra 255 to whatever string space you think you are using, since no string can ever be larger than 255. I guess I’ll have to test that sometime, too.
Next time, I’ll get back to my point…