Completing a BASIC language interpreter in 2025
Although my interpreter was already pretty fast and with enough statements to build games, I wasn’t satisfied because it still missed one thing that the ECS BASIC implements: strings. Only two, A$ and B$, with GET and PUT, for things like getting a name from the keyboard or showing a name.
I thought about strings for four days, then I decided to code things like I know what I was doing. I added a string stack pointer bas_strptr where any created string is added.
The first thing to implement was an array for the string variables (>A$–Z$) each one pointing to the current string contained (or zero if none). I modified the whole of the expression parser to insert the type in the Carry flag (Clear = it is a number, Set = it is a string), then I made the first string support in the language where it detects if a string name appears (letter plus the $ sign) and reads it and copies it to a new string on the stack, returning this pointer as expression value (and of course the carry flag set)
The next step was assigning string variables, it simply took the pointer and stored it into the respective string variable pointer. Of course, I was afraid that I was creating a monster because I wasn’t planning for the garbage collector.
Then I went full-steam ahead and put support in INPUT, PRINT, and added the concatenation of strings using the plus operator, also the functions ASC, CHR$, LEN, LEFT$, RIGHT$, MID$, INSTR, VAL, and STR$.
Now I had string support in my BASIC language for the ECS, but at some point in the execution it would fill up the stack, and crash with an “Out of Memory” error.
Garbage collection
It was kind of crazy having a BASIC with string support but no garbage collection. I needed a way to copy strings into arrays, and delete the work-in-progress strings created as expressions are evaluated.
It would be easy when having a C memory management system as you only have to replace the pointer, and free the original. But any memory management comes with headers and linked lists, extra memory requirements, and slowness. Given the Intellivision CP1610 processor is already slow enough (894 khz), I decided against it.
However, I noticed that temporary strings are only created inside the expression parser. So what about a double stack? One stack for strings in variables, and one stack for temporary strings.
I added a secondary pointer bas_strbase (I like how it sounds like star base)
At the start of each statement, bas_strbase is copied to bas_strptr (thus effectively erasing the temporary strings) A problem needed to be solved: growing bas_strbase on each string assignment.
I was going to implement the most simple solution: go over the 26 string variables doing comparison and movement of pointers, and insert the new string in its place.
Just as I was coding this, I noticed I had an easier solution. As I was working with 16-bit words, not all values are used. I could use a value like 0xcafe to mark a non-used space, and boom! I had an idea.
When doing the assignment, delete the original string (fill it with 0xcafe words), now explore the strbase area to find a string of 0xcafe words big enough to save the new string.
The better part is when there is no space for the string, I simply copy the string pointer as the new bas_strbase pointer (effectively growing the base memory area), and all words between the end of the string and the previous bas_strbase pointer (ahead in memory) are filled with 0xcafe words.
Full string support with garbage collection at very small price of performance. Exactly what a CP1610 processor needs.
STRING_TRASH: EQU $CAFE ; ; String assign. ; R1 = Pointer to string variable. ; R3 = New string. ; string_assign: PROC PSHR R5 MVII #STRING_TRASH,R4 ; ; Erase the used space of the stack. ; MOVR R3,R2 MVI@ R2,R0 INCR R2 ADDR R0,R2 MVI bas_strbase,R0 CMPR R0,R2 BC @@3 @@4: MVO@ R4,R2 INCR R2 CMPR R0,R2 BNC @@4 @@3: ; ; Erase the old string. ; MVI@ R1,R2 TSTR R2 BEQ @@1 MVI@ R2,R0 MVO@ R4,R2 INCR R2 TSTR R0 BEQ @@1 @@2: MVO@ R4,R2 INCR R2 DECR R0 BNE @@2 ; ; Search for space at higher-addresses. ; @@1: MVII #start_strings-1,R2 CMP bas_strbase,R2 ; All examined? BNC @@6 ; Yes, jump. @@5: CMP@ R2,R4 ; Space found? BNE @@7 ; No, keep searching. CLRR R5 @@8: INCR R5 DECR R2 CMP bas_strbase,R2 BNC @@9 CMP@ R2,R4 BEQ @@8 @@9: INCR R2 MVI@ R3,R0 INCR R0 CMPR R0,R5 ; The string fits? BNC @@7 ; ; The string fits in previous space. ; MOVR R3,R4 MOVR R2,R5 MVO@ R2,R1 ; New address. @@10: MVI@ R4,R2 MVO@ R2,R5 DECR R0 BNE @@10 PULR PC @@7: DECR R2 CMP bas_strbase,R2 BC @@5 ; ; No space available. ; @@6: MVO R3,bas_strbase ; Grow space for string variables. MVO@ R3,R1 PULR PC ENDP


Example of a parser for a text adventure game using string functions.
Going mathematic
Since my floating-library was complete with the four operations, I had an ace under the sleeve: I already had tested sin and cos functions with it, but for some reason these had a bug. For sin(1°) the resulting value was 0.0172.
After a whole day examining the operation instruction-by-instruction (the jzintv debugger shines here), I discovered that I did a comparison in the wrong way and corrected it.
I was so happy that I went immediately to port the remaining mathematical functions (ATN, TAN, LOG, EXP, and derived SQR and the power-of ^ operator). There were no pitfalls along the way, except one, my BASIC has a mantissa with one extra bit of precision, and EXP(LOG(64)) returned 63.999999
Both operations do a multiplication with a constant (log does it at the end, and exp in the start). I noticed that the value was misrounded for 25 bits of mantissa, so I calculated a better constant, and et voila! EXP(LOG(64)) returned 64.
Making it easier for the user
A lot of BASIC interpreters in the eighties didn’t supported instructions for graphics. The Commodore 64 was particularly known for requiring POKE for almost anything, unless you had the somewhat expensive Simon BASIC cartridge.
However, in the Intellivision you have few graphics capabilities. In the Color Stack mode you have something called Colored Squares. This means each tile on the screen (20×12) can have four colors. This means a bloxel resolution of 40×24, and each one can have one of eight colors (one being the background).
I implemented PLOT with these limitations, and also added PRINT AT (for putting text at any screen position), and TIMER to measure time.
One of the most difficult things was implementing the floating-point number parsing. I finally decided to approach it like parsing an integer, taking note of the number of digits parsed, and take note of the position of a period. Once it reaches the biggest number it can represent (9,999,999) then it starts ignoring any further digit (but it keeps counting them)
The final calculation step is to multiply it, or divide it taking in account the period position. Also taking in account any exponent present (for example, e+1 or e-3)
It wasn’t so expensive in computation time. I added along a FRE(0) function to know how much space remains for writing programs.
It is the eighties
Let’s suppose we are working to make this BASIC interpreter really useful for the Mattel ECS. We still need two things: cassette, and printer.
Fortunately, a lot of people at Atariage Forums have worked along the years to decipher the ECS hardware (thanks to intvnut, lathe26, and decle)
The ECS contains the hardware to interface to a cassette recorder/player at 300 bauds with FSK (Frequency-Shift Keying) of 2400/4800 hz (technically this is a modem) and it also includes a UART (Universal Asynchronous Receiver/Transmitter) patterned losely after a Motorola MC6850, but the frequency selector is separated, allowing to turn on/off a relay (cassette remote control), and to switch between two ports (the cassete and the AUX port for the printer)
Now for the cassette, I was going to use 300 bauds, this means around 30 characters per second. Do you remember your 56K modem? It was 186 times faster! I needed to optimize my BASIC as I was using token numbers above $0100, so I moved them to the area $0080-$00ff. Now all the words are only used in the lower 8 bits, and the tokenized program can be saved as bytes.
I was very happy when these cassette routines worked in emulation, and I ordered cables from Amazon for trying to record and play in my cellphone.
For some reason probably related to audio levels and automatic compression, I could record audio from the ECS in my cellphone, but playing it back never resulted in anything.
I was tired, and I decided to try my PC. I connected the ECS to the Mic In, and Line Out, and same problem. Besides the Windows utilities make amazingly hard to change the source and playing line. I got the Audacity program, and it has the line input/output options easily selectable. Again no results.
I wrote a small program to read the UART continuously, and I couldn’t see anything. I decided to try the Audacity’s amplify effect, and et voila! My UART program started throwing decoded bytes. I stopped the program, and I tried the VERIFY command (remember I had just saved the same program), but it didn’t worked. Worst, when I ran again my test program, I didn’t got any data!
I revised my setup values for the UART, but nothing. I was mystified for some hours until I got memories of a chip that basically went nuts if you accessed it too fast. Could it be that? Is the CP1610 so fast? I added a delay after every access to the UART chip.
I typed again my test program, I did SAVE, recorded on the PC, amplified it, I RUN my program, and played the audio back. Ok, UART was reading things. Now I stopped the test program, I did VERIFY, and I played the audio back from my PC. The longest 20 seconds of my life. And it worked!
Immediately I resetted the ECS, losing the program, and I did LOAD (of course, playing back the audio), and again it worked!

My UART test program after a successful LOAD statement.
Notice that although it saves BASIC programs, these programs aren’t compatible with the original ECS BASIC because it is a completely different BASIC language.

Reversi game from the Tim Hartnell’s Giant Book of Computer Games book working with my BASIC interpreter for the Mattel ECS.
The printer is in the room
While the printer was in shipping process, I implemented LLIST, and LPRINT. I modified the core of both statements to access the output through an indirect function. So you only change the pointer to target the screen or the printer. I detected here a bug in jzintv that prevents it from outputting the printer data to a file.
I got a Mattel Aquarius along the printer a few days later. I had to clean it because it was pretty dusty. The printer doesn’t have the top cover that protected the paper roll, but it included a paper roll, and fortunately it still had the cylinder that helps the papel to roll.

Mattel Aquarius computer with expansion board, two games, cables, and the Aquarius printer.
I adjusted the paper, powered on the printer, and verified I could advance the paper (having a working motor is 90% of the printer).
I built the cable with the instructions from lathe26’s article, and the first time it didn’t worked (I grounded the CTS cable accidentally), but after correcting it, I expected trash for my first print, instead, I got a pretty nice printing!
Of course, I couldn’t resist printing some listings, and a sine wave.

The printed source of my UFO game. The paper roll is really old.
What remains to do?
I added the DRAW and CIRCLE statements, and POINT functions to complete the graphics support. These are enough to make some nice games without using sprites. I made a graphics demo for filling the screen with lines, and I noticed my pseudo random number generator didn’t covered the screen, so I had to improve it.

DRAW program for my ECS BASIC interpreter.
Also I added the POS and LPOS functions to know the horizontal position of the cursor. The SPC and TAB functions for PRINT. Plus a HEX$ function to ease system programming.
In the tokenization table, I added placeholders to expand the language and don’t break compatibility with any cassette tape being created.
With this it has become a full-fledged BASIC interpreter for the Mattel ECS that uses 19 kilowords, instead of the 24 kilowords of the slow and limited ECS BASIC interpreter.
I don’t see anything more I could do in the near future, except maybe expanding the editor to be a full-screen editor. Currently, it is a line editor that reads its input from the screen.
At this point, it is a fun experience the process of typing BASIC programs in the ECS, and watch the results back. You can save the programs, or print it. And of course, you can only imagine the success that Mattel Electronics would have enjoyed if they put together a good BASIC with its Mattel ECS.
Small statistics of the assembler code:
- basic.asm: 5333 lines.
- fplib.asm: 718 lines.
- fpio.asm: 462 lines.
- fpmath.asm: 516 lines.
- uart.asm: 341 lines.
- Total of 7370 lines of assembler code written between Sep/17 and Oct/12, around 300 lines written daily.
Related links
Last modified: Oct/12/2025