Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

Color BASIC program line info dump program

These days, I feel like I am regularly saying “I’ve learned more this week about X than I learned in Y years of using it back in the 1980s!”.

This is another one of those.

Each line of a Color BASIC program is tokenized (changing keywords like PRINT to a one or two byte token representing them) and then stored as follows:

  • 2-Bytes – Address in memory where next line starts
  • 2-Bytes – Line number (0-63999)
  • n-Bytes – Tokenized program line.
  • 1-Byte – Zero (0), indicating the end of the line

The four byte header and the 1 byte zero terminator mean that each line has an overhead of 5-bytes. You can see this by printing free memory and then adding a line that has a one byte token, such as “REM” or “PRINT”:

Above, you see the amount of memory decreases by 6 bytes after adding a line. That’s five bytes for the overhead, and one byte for the “REM” token.

The BASIC program starts in memory at a location stored in memory locations 25 and 26. You can see this by typing:

PRINT PEEK(25)*256+PEEK(27)

There are other such addresses that point to where variables start (directly after the program), and where string memory is. Here is an example program from an earlier article I wrote that shows them all. (The comments explain what each location is.)


20 ' PEEK(25)*256+PEEK(26)

40 ' PEEK(27)*256+PEEK(28)

60 ' PEEK(29)*256+PEEK(30)

70 ' END OF ARRAYS (+1)
80 ' PEEK(31)*256+PEEK(32)

100 ' PEEK(33)*256+PEEK(34)

120 ' PEEK(35)*256+PEEK(36)

140 ' PEEK(39)*256+PEEK(40)

160 PRINT "PROG  SIZE";(PEEK(27)*256+PEEK(28))-(PEEK(25)*256+PEEK(26)),;
170 PRINT "STR SPACE";(PEEK(39)*256+PEEK(40))-(PEEK(33)*256+PEEK(34))
180 PRINT "ARRAY SIZE";(PEEK(31)*256+PEEK(32))-(PEEK(29)*256+PEEK(30)),;
190 PRINT " STR USED";(PEEK(39)*256+PEEK(40))-(PEEK(35)*256+PEEK(36))
200 PRINT " VARS SIZE";(PEEK(29)*256+PEEK(30))-(PEEK(27)*256+PEEK(28)),;
210 PRINT " FREE MEM";(PEEK(33)*256+PEEK(34))-(PEEK(31)*256+PEEK(32))

I thought it might be interesting to write a BASIC program that displays information on each line of the BASIC program. That information would include:

  • Start address of the line
  • Address of the next line
  • Line number of the line

Here is what I came up with. It can use generic PRINT in lines 40 and 70 (for Color BASIC) or a nicer formatted PRINT USING (for Extended Color BASIC) in lines 50 an 80.

20 L=PEEK(25)*256+PEEK(26)
30 NL=PEEK(L)*256+PEEK(L+1)
50 PRINT USING"##### #####";L;NL;
70 'PRINT PEEK(L+2)*256+PEEK(L+3);NL-L
80 PRINT USING" ##### ###";PEEK(L+2)*256+PEEK(L+3);NL-L
90 L=NL:GOTO 30

For this program, as shown, running on a virtual 32K Extended Color BASIC CoCo in the XRoar emulator, I see:

The first column (ADDR) is the address of the BASIC line in memory. After that is the address of where the next line begins (NADDR), and it will match the address shown at the start of the following line. The third column is the line number (LINE#), and last is the size of the line (SIZ) which includes the tokenized line AND the terminating zero byte at the end of it.

The final line has a “next address” of zero, indicating the end of the file.

At the start of the program I included three comments:


In the output of the program, you see them described as:

 9729  9747     0  18  <- [0 'BASINFO.BAS]
 9747  9765     1  18  <- [1 REM BASINFO.BAS]
 9765  9782     2  17  <- [2 REMBASINFO.BAS]

You can see that the length of lines 0 and 1 are both 18, even though one looks like it should be shorter. In this case, the apostrophe (‘) abbreviation for REM seems to take as much space as “REM ” (with a space after it). This is because the apostrophe is encoded as a “:REM” (colon then REM). Alex Evans recently reminded me of this. This behavior would allow you to use it at the end of a line like this:


…instead of having to do:


But don’t do either! REMs at the end of the line can be the worst place to have REMs, since BASIC will have to scan past them to get to the next line, even if they are after a GOTO. This makes them slower. (Reminder to self: do an article on this since I’ve learned more since I original covered the topic in one of my Benchmarking BASIC articles…)

But I digress…

If you wanted to run this on your own program, you could do so by making this routine load at a high line of BASIC (higher than any lines you might be using), then you could save it as ASCII (SAVE”BASINFO”,A) and then use MERGE”BASINFO” (from disk) to bring those lines in to your program.

63001 NL=PEEK(L)*256+PEEK(L+1):PRINT USING"##### #####";L;NL;:IF NL=0 THEN END ELSE PRINT USING" ##### ###";PEEK(L+2)*256+PEEK(L+3);NL-L:L=NL:GOTO 63001

Now you could do RUN 63000 to see what your program looks like. (The highest line number Color BASIC allows is 63999 so you could change that to 63998 and 63999 if you wanted absolutely the most line numbers available for your program ;-)

You could also add “IF L=63000 THEN END” somewhere and have it stop when it hits that routine.

What use is this?

For an upcoming article, I expect to use a version of this code to “prove” something as it relates to BASIC and the length of lines.

But, it might also be fun to generate some statistics — longest line, shortest line, a graph of the different line lengths, etc.

Until next time…

FreeRTOS: Virus?

The discussion about using FreeRTOS has come up at my day job in recent years, so a month or two ago I downloaded it from the official website with plans to take a look at it.

I never got around to it, but today our IT department contacted me about its removal from my system. The download was flagged to contain a virus. I am sharing this information here so it will show up in search engines:

Severe Gen:Variant.Tedy.307215 - Virus.Generic - Deleted

File - C:Users<username>DownloadsFreeRTOSv202212.00FreeRTOSv202212.00FreeRTOSDemoWIN32-MSVCDebugRTOSDemo.exe

md5: 2F1699A8E9DE9D946FD6DDCC4BCC2F45

It appears all I did was open the project and build it in Visual Studio. Nothing else on the system was flagged, and we use Visual Studio to build two of our in-house apps, so I am curious why this happened.

Anyone seen this? Please leave a comment.

Stay safe :)

An adventure game rabbit hole…

From the “this is how my mind works sometimes” department…

Some definitions, as they relate to my career as an embedded programmer:

  • Can of Worms – when you go to fix something that should be simple and quick, but as you try to fix it, you realize it’s tied to other things that will also need fixes and thus your one quick fix turns in to days of many not-so-quick fixes.
  • Whac-A-Mole(tm) – when you fix a bug only to find that it manifests another bug, and then you fix that bug and it causes yet another bug, and then… (See also: Unintended Consequences)
  • Rabbit Hole – when you think something will be simple, then you look, and have to go to another section of code to find what you are looking for, but that sends you to another section of code which also sends you to another section of code and that…

Today, a quick rabbit hole in relation to text adventure games.

Down the rabbit hole: the beginning of playing

I was fascinated with text adventure games when I first learned of them back around 1981 or 1982. I do not recall what the first one I saw was, but it was likely something at the local Radio Shack playing on a TRS-80 Model 3. I definitely remember buying Mission Impossible by Scott Adams on a cartridge for my Commodore VIC-20.

This led me to trying to write my own text adventures. The “most successful” one was probably a custom game I wrote for the *TALK TO ME* BBS in Houston, Texas. The game was a recreation of the SysOp’s (system operator) apartment, and the goal was go roam around the apartment collecting parts of the system that ran the BBS and hook them all back up. It was a very simple game, with the only challenge being something random you couldn’t do anything about — a killer cockroach (if I recall) that would randomly show up and scatter all your work back randomly across the apartment. Randomly. One day, I hope to find this program on a cassette tape somewhere.

But I digress…

Down the rabbit hole: the beginning of coding

Over the years, I wrote various routines for doing text adventures — word wrap, verb/noun input, moving from room to room, getting and dropping objects, etc. When I started going through my old CoCo stuff years ago, I found notes on old games I’d forgotten I had been working on. The most complete one was “The ODDyssey” which I was co-writing with my MC-10 buddy Paul T. (We had intended it to run on both CoCo and MC-10.) I have the engine of that game and the complete map, but we never finished the goals and objects beyond some early test ones.

Still, it looked pretty good, and using a trick I think I read about in The Rainbow magazine, you could type in “get the black book” instead of just “get book.”

The ODDyssey, my uncompleted text adventure from around 1984.

In addition to various CoCo adventures, I also tried writing one in Java that would run as an applet on a web browser. That game, based on Disneyland/Disney World’s Haunted Mansion, even included background music :)

Heck, I even wrote a “WebVenture Generator” MS-DOS program that would let me make simple walk-through web pages using digital photos I took. Here is one from Disneyland using photos I took in August 1996:

None of this has anything to do with today’s topic except to say that I’ve toyed with adventure games numerous times over the years in several different languages.

Down the rabbit hole: the beginning of the point

When I’m starting a new adventure game, I usually make a test map. Ideally I have rooms that have exits in different directions so I can verify all of that works. It might start as something very simple like this:

[ 1 ] --- [ 2 ]
  |         |
[ 3 ] --- [ 4 ] --- [ 5 ]

That has rooms that have exits leading North, South, West and East, but it doesn’t truly exercise all the possibilities. A proper “engine” shouldn’t care, but if one wanted to be thorough, you’d want to make sure you have every possible combination of room exits covered in a test map.

How many exits can one room have?

For simplicity, we will limit our exits to the four compass directions – North, South, West and East. A fancier adventure game might allow diagonals (Northwest, Southeast) and vertical (Up and Down). But for now, we’ll stick to the four basic directions.

Initially I started drawing out a map by hand, trying to make sure I had a room with only an exit East, a room that had only an exit West, etc. I quickly lost track of what I was trying to do…

So I wrote a program to show me all the combinations. Since there are four exits that can either exist or not exist, I decided to use four bits to represent them.

  • Bit 0 – North
  • Bit 1 – South
  • Bit 2 – West
  • Bit 3 – East

Side Note: I am using NSWE here, but I recall, when I first learned about writing adventure games from some VIC-20-era magazine I had, they used the order of NEWS – North, East, West, South. That might be easier to remember, but the order doesn’t matter for this example.

Here are what those bits would look like:

0001 - Exit North
0010 - Exit South
0100 - Exit West
1000 - Exit East

…then all the other combinations. Once I thought about it this way, it was easy to see I had everything from 0000 (0, no exits) to 1111 (15, exit in all four directions). Thus, there would be 16 possible room types.

I wrote this program to print them all out:

20 FOR D=1 TO 15:PRINT D;
70 PRINT,;

Side note: If you wanted to include the diagonal directions of NW, NE, SE and SW, you’d expand that to eight bits. If you were also including Up and Down, add two more bits. That would make 1024 possible room types — and now you see why I limited it to just four directions.

And from there, I started drawing a map, making sure that I had at least one room of each of the 15 times listed above. (A room with no exits would also be possible, though maybe not useful.)

And this led me even further down this rabbit hole… I wanted to make the most compact map I could that had a room of each type in it. That would be my “test map” for the adventure game project.

Down the rabbit hole: the beginning of brute force

After initially trying to map out all the combinations using brute-force…

[10]-[6]  [2]  [10]-[4]
 |    |    |    |
[3]  [11]-[15]-[7]
 |    |    |    |
[1]  [9]--[15]--[5]

TODO: 13 NWE, 14 SWE

…I wondered if there might be an easier way. Instead of just spending ten minutes drawing this by hand, I could probably spend a few hours or days to come up with a program that would help me create the map.

And as I thought about that, it sorta sounded like one of those sliding puzzle games I enjoyed as a kid.

Micha L. Rieser, via Wikipedia

In a number puzzle, the goal is to get the numbers in order. For a photo puzzle, it was to get the photo assembled. For my project, there could be multiple solutions — just as long as all rooms had a/an exit(s) that connected to another room’s exit(s).

And that reminded me of a game I had for my Sega Genesis called Junction. Or the relatively new CoCo game Pipes by Nick Marantes.

In those games, you are either sliding pieces around to complete a track for some object to follow, or you are placing pieces down (ala Tetris) to create a track.

For my use, instead of having the “playfield” be locked to some fixed width and height, it could be anything — very wide, very tall, or both. The more compact, the higher the score. The score might be calculated by the fewest number of duplicate pieces (15 being the least you could use, since you had to have at least one of each room type) and the least amount of empty spaces in the final map. For example, if there were only four pieces, they could be represented in a 4×4 grid with no empty spaces — a perfect score:

 |    |

But if the same four pieces had been represented like this:

[1]--[2]  xxx
xxx  [3]--[4]

…it is now using a 3×2 grid with two empty spaces and that would be a lower score. Somehow.

And if I could create something like this, and get people to play it, they could work out the best and most efficient way(s) to represent a text adventure demo map that covered all possible room types.

And that, my friends, sounds like even more work than just brute forcing a map until I am happy with it.

So naturally, I may just have to do this.

Until next time…

POKE 113,0:EXEC 40999 (and DLOAD)

If you recognize this, you must have been a Radio Shack Color Computer owner. If not, these two instructions would reboot a CoCo, starting it back over at the boot up screen just like when it is first powered on.

When the CoCo 3 was introduced, a different shortcut for rebooting was discovered thanks to a hidden easter egg. The egg was a hidden graphic showing a digitized photo of there three programmers who worked on the BASIC ROM. To make it display, you held down CTRL and ALT while powering on the machine. (A nod to Ctrl-Alt-Del reset on a PC, perhaps?)

We quickly discovered that the easter egg would also show if you held down CTRL+ALT and hit the reset button. Once the image was on the screen, pressing the reset button would reboot the computer back to the startup screen!

Thus, on a CoCo 3, POKE 113,0:EXEC 40999 was replaced by “CTRL+ALT+RESET, RESET” saving much typing.

But I digress…

Have you ever wondered just what that POKE and EXEC were doing? Neither did I, but I decided to find out anyway.

POKE 113,0

To figure this out, I turn to the book Color BASIC Unravelled by Spectral Associates. This book contains a disassembly of the Color BASIC ROM, fully commented with explanations of what everything does.

113 in decimal is hexadecimal &H71, which we need to know because the disassembly uses HEX instead of decimal for memory locations. Looking at memory location &H71 shows us this:

Spectral Associates named it RSTFLG (Reset Flag) and based on the value there, the system will either WARM start or COLD start when the reset button is pressed. By default, this value has &H55 there:

When the reset button is pressed, the CoCo’s 6809 processor jumps to a reset vector that is in memory at the last two bytes — &HFFFE-&HFFFF. Whatever address is stored there will be where the 6809 starts executing code. This address comes from the last two bytes of the 8K Color BASIC ROM at memory location &HBFFE-&HFFFF. For “reasons” beyond the scope of this article, the upper memory is ghosted so that value also appears at &HFFFE-&HFFFF.

Thus, when reset is pressed, execution begins at whatever address RESVEC is which you can see in the left columns of the disassembly is at &HA027 in the Color BASIC ROM. Searching in the Unraveled book for &HA027 (or better, the RESVEC label) shows us initialization code that eventually jumps to this:

There you can see an LDA RSTFLAG which is loading register A with whatever is at memory location 113 and comparing it to &H55. Based on the result, it either jumps to the cold start routine or warm start routine.

POKE 113,0 just puts the 0 in there so the code ends up jumping to BACDST which is what starts everything up and displays the BASIC copyright screen.

Thus, if you do POKE 113,0 and then press RESET, the CoCo will cold start.

EXEC 40999

By now, you may realize what code is at 40999. In HEX that is &HA027 … does that look familiar? That’s the address that the reset vector points to, which eventually leads to the routine at &HA00E which checks the RSTFLG (memory location 113) to decide what to do.

EXEC 40999 just jumps to the location that the reset button would causes the 6809 to jump to.

Thus, POKE 113,0 sets the “cold start” flag, and EXEC 40999 (or pressing reset) invokes the code that checks that flag and acts upon it.

You are welcome.

Bonus – DLOAD

There was another shortcut discovered on the CoCo 3 which allowed a cold start to be done without using EXEC 40999. Instead, you can type:


…and the CoCo 3 will cold start. Typing DLOAD by itself will cause the CoCo 3 to pause and then clear the screen. Any guess at what is going on?

DLOAD was a command that came with Extended Color BASIC, and it is a command I recall using exactly one time back then, though I later used it often on the CoCo 3 — just not for its original purpose.

I do not have a copy of the original TRS-80 Color Computer Getting Started with Extended Color BASIC manual for the CoCo 1, but since DLOAD is not mentioned in the 1984 version of the same manual for the CoCo 2, I suspect it may not have ever been documented in those manuals. (Leave a comment if you have information about this.)

It is, however, listed in the spiral-bound TRS-80 Color Computer Quick Reference Guide that I probably consulted more than any other book I have ever owned.

DLOAD was similar to the cassette CLOAD command, except it loaded via the Serial I/O port at either 300 or 1200 baud. If you had a serial cable connected to another CoCo, or a modem dialed in to another CoCo, it was possible to DLOAD a program that way.

And this is how I downloaded my first real terminal program on my CoCo 1 — someone sent it to me via DLOAD!

The only problem with this command is that there was no DSAVE command built in, so the only way to upload to DLOAD was using a special program. I no longer recall what that program was, or who wrote it, or where it came from. (If you know, please leave a comment.)

In Extended Color BASIC Unravelled, there is this note about DLOAD:

DLOAD is the most obscure command in the Color Computer and absorbs a substantial amount of space in the ROM. DLOAD is so poorly understood because Tandy has never made the necessary companion routine, DSEND. DLOAD will DOWNLOAD a file over the RS 232 line from another system, however there is no companion routine, which will transmit a file over the RS 232 line to another Color Computer. Once a DSEND routine is built and made available to the masses, DLOAD will be much better understood.

– Extended Color BASIC Unravelled, page 5.

From looking at the source, it appears there was both DLOAD and DLOADM variations, just like there is CLOAD/CLOADM for tape and LOAD/LOADM for disk, with the “M” version being for machine language programs. If this is true, then that Quick Reference manual entry is incorrect and DLOAD does not load a machine-language program — it may have loaded a BASIC program, with DLOADM being the way to load machine language over the Serial I/O port.

But I digress.

But what does this have to do with the CoCo 3 and resetting the computer? I’m glad you asked! (You did ask, didn’t you?)

The lack of a DSAVE command (or DSEND as Unravelled called it, not matching the other existing commands) meant that DLOAD really wasn’t that useful. Heck, without it being documented in the manual, how would anyone even know it existed? This may have something to do with this command being removed from the CoCo 3. The memory for that command was repurposed for other patches to Extended Color Basic needed to support new features of the CoCo 3’s hardware.

DLOAD started at &H8C1B in the Extended BASIC ROM. On the CoCo 3, this space is used for a different purpose. Super Extended BASIC Unravelled has this note:

The initialization routine for the Color Computer 3 begins at $8C1B. This code writes over the DLOAD routine that was in the original Color Computer . . .

– Super Extended BASIC Unravelled, page 30

In the disassembly, that location is still labelled as DLOAD, and the original BASIC keyword of DLOAD is still in Extended BASIC pointing to it. BUT, the CoCo 3 (which copies all the ROMs in to RAM on startup) patches that location with new code. It also modifies the Interrupt Vectors so the RESET vector points there instead of the original routine in the Color BASIC ROM:

Thus, on a CoCo 3, pressing reset jumps to the location in memory where the DLOAD code used to be, but which is now the reset routine for the new CoCo 3 hardware.

The complete quote from the Unraveled book is actually…

The initialization routine for the Color Computer 3 begins at $8C1B. This code writes over the DLOAD routine that was in the original Color Computer (actually, typing DLOAD will simulate pressing the reset button). This initial- ization routine is used for both a warm start (simply getting control of the computer back from a runaway program) and a cold start (where the computer and Basic have to be reinitialized).

– Super Extended BASIC Unravelled, page 30

POKE 113,0:DLOAD on a CoCo 3 performs the same function as POKE 113:EXEC 40999 on a CoCo 1 or CoCo 2.

But … there is something different. If you do POKE 113,0:DLOAD on a CoCo 3, you get a short pause before the system cold starts. If you do POKE 113,0:EXEC 40999, it quickly restarts.

This is because EXEC 40999 is still jumping in to the original &HA027 routine in the Color BASIC ROM code. This bypasses all the new cold start code for the CoCo 3! Doing the old style POKE/EXEC may look like it reset the CoCo 3, but it wasn’t actually doing everything and, possibly, there could have been some instances where hardware configurations had changed that this would not reset/restore properly.

I most certainly did not realize that back then. I suppose the use of POKE 113,0:DLOAD (or POKE 113,0 then reset) was actually the “more appropriate” way to reset the CoCo 3, which is probably how I learned about it somewhere.

And now you know … more than you wanted to … about POKE 113,0 and EXEC 40999 and DLOAD on a CoCo 3.

Until next time…

P.S. While researching for this article, I started walking through all the things that go on during the CoCo 3 startup. It’s quite a lot with all the ROM to RAM and patching going on. Maybe I’ll have to dig in to that and write about it someday.

Synology NAS, DSL Modem firewalls, and OpenVPN

Ages ago, I started my blog mostly with the goal of posting research items that I couldn’t figure out by web searches. I figure, maybe someone else will be searching for the same thing one day and run in to my efforts and together we can figure it out.

This post is for that reason, so feel free to skip it. I have the solution, but it will not be in this post since I do not have the details with me at the moment.

Synology DS1522 NAS

The Synology NAS devices can install software, and support three types of VPNs. Choosing which one to use is a rabbit hole, but OpenVPN seems pretty common and cross platform.

OpenVPN can be enabled on the NAS, but if it is inside a network, you cannot access it. Some routers are directly supported by the NAS and it can open up holes in the router’s firewall (UPnP) and, I suppose, it just magically works. (We should really all turn that feature off, because if something naughty gets inside your network, it could potentially do the same, opening up your private network to the outside world.)

If you NAS is behind a cable modem, DSL modem, etc. you may have to manually open up ports and forward them to the IP address of your NAS.

Once that is done, a profile can be installed on a PC, Mac, Linux, iPad, Android, etc. and then you can run a VPN app and connect to your NAS. It can selectively allow access to other things inside your home network.

I plan to document some simple steps to make this happen, and save hours of watching YouTube videos and reading knowledge base articles.

But for now, I wanted to post this to get something in the search engines. In my case, I’ll be mentioning a specific CenturyLink DSL modem.

To be continued…

XRoar emulator ctrl/alt/f1/f2 keyboard config for CoCo 3

NOTE: This information requires XRoar 1.3.1 or later, as earlier versions had an issue remapping the Control key on Mac.

The XRoar emulator added support for the CoCo 3, but by default, the new CoCo 3 keys (alt, ctrl, f1 and f2) do not work for me — at least on the Mac. The author, Ciaran, pointed me to the “kbd-bind” configuration option. This allows remapping a host computer key and passing it through to the emulated machine. In my case, I want to do the following:

  • F1 key – F1 key on CoCo 3
  • F2 key – F2 key on CoCo 3
  • Left Control – CTRL key on CoCo 3
  • Left Option – ALT key on CoCo 3

The XRoar manual has the following information about kdb-bind:

-kbd-bind hkey=[pre:]dkey … Bind host key hkey to emulated key dkey.

When binding keys with -kbd-bind, if the emulated key dkey is prefixed with ‘preempt:’ or ‘pre:’, this binding preempts translation; useful for modifier keys. Interpretation of hkey depends on which user-interface toolkit is in use, and it might be useful to run with -debug-ui 1 to see what the toolkit calls your host keys.

Special values for dkey are: ‘colon’, ‘semicolon’, ‘comma’, ‘minus’, ‘fullstop’, ‘period’, ‘dot’, ‘slash’, ‘at’, ‘up’, ‘down’, ‘left’, ‘right’, ‘space’, ‘enter’, ‘clear’, ‘break’, ‘escape’, ‘shift’, ‘alt’, ‘ctrl’, ‘control’, ‘f1’, ‘f2’.

– XRoar manual

The “hkey” is the name of the key you are trying to remap, and I think it may be different between Mac and PC. By running xroar from the command line with the “-debug-ui 1” option, the terminal will print out keys as you type inside the emulator. I did this, and press the keys I wanted to remap.

% xroar -debug-ui 1

(...stuff omitted...)

sdl.key press   scan=224   sym=400000e0   mod=0040   unicode=0000   name=Left Ctrl
sdl.key release scan=224   sym=400000e0   mod=0000   unicode=0000   name=Left Ctrl

sdl.key press   scan=226   sym=400000e2   mod=0100   unicode=0000   name=Left Option
sdl.key release scan=226   sym=400000e2   mod=0000   unicode=0000   name=Left Option

sdl.key press   scan= 58   sym=4000003a   mod=0000   unicode=0010   name=F1
sdl.key release scan= 58   sym=4000003a   mod=0000   unicode=0000   name=F1

sdl.key press   scan= 59   sym=4000003b   mod=0000   unicode=0010   name=F2
sdl.key release scan= 59   sym=4000003b   mod=0000   unicode=0000   name=F2

Above, I can see that the “hkey” for pressing my Mac’s left control is “Left Ctrl“. My left option key is “Left Option” (spelled out, and not abbreviated like Ctrl was). F1 and F2 were simply “F1” and “F2“.

I went with Control and Option on the left side, since I have duplicates of those keys on the right side of my keyboard. I can remap just the left ones for CoCo 3 use, and still have the right ones for XRoar interface use.

NOTE: You could remap any key — like make F5 turn in to the CoCo 3’s F1, or whatever.

To remap those keys, I can pass the option in from the command line like this:

xroar -kbd-bind "Left Ctrl"=pre:ctrl -kbd-bind "Left Option"=pre:alt -kbd-bind F1=pre:f1 -kbd-bind F2=pre:f2

Or, I can place the following entries in the xroar.conf file:

kbd-bind "Left Ctrl"=pre:ctrl
kbd-bind "Left Option"=pre:alt
kbd-bind F1=pre:f1
kbd-bind F2=pre:f2

I actually made a special xroar.conf entry so I could get a customized CoCo 3 with RGB monitor:

machine coco3rgb
    machine-desc CoCo 3 RGB
    machine-arch coco3
    tv-input rgb
    kbd-bind "Left Ctrl"=pre:ctrl
    kbd-bind "Left Option"=pre:alt
    kbd-bind F1=pre:f1
    kbd-bind F2=pre:f2

If I run XRoar using the command line, or with those entries in the xroar.conf file, I can now boot in to the CoCo 3, and hold down my Left Control+Left Option then go to Hardware->Soft Reset and get the hidden CoCo 3 easter egg photo:

Though, the border color does not match that of a real CoCo 3, I don’t think.

I hope this helps someone else trying to use the XRoar in CoCo 3 mode with those four special CoCo 3 keys.

Until next time…

High-resolution spiraling in Extended BASIC

I figured out how to do this.

Change the step value in line 5 to make the spiral tighter (.2) or wider (.05).

5 ST=.1
20 FOR R=1 TO 165
30 CIRCLE(128,96),R,1,1,S,S+ST
40 S=S+ST:IF S>1 THEN S=S-1
999 GOTO 999

As an experiment, I switched it to PMODE 0 so there could be eight graphics pages of spirals which could be page flipped to make a cool hypnotic animation.

I miss the fun of BASIC.

6847T1 Test Program (for late model CoCo 2s)

I never shared this test program here, it seems, but you can find the full article over on Vintage is the New Old. This program will work on a CoCo 2 (or emulator) that has the later 6847T1 VDG chip. If the CoCo 2 has a slash in the zero on the screen, that’s the T1 chip.

0 REM 6847T1 VDG &HFF22
0 ' BIT 0) 01
1 ' BIT 1) 02
2 ' BIT 2) 04
3 ' BIT 3) 08 BG COLOR
5 ' BIT 5) 20 REVERSE
6 ' BIT 6) 40 BORDER=BG
7 ' BIT 7) 80
10 CLS:PRINT "6847T1 VDG Demo"
20 L=&HFF22

100 ' NORMAL
110 PRINT "Normal"
120 REM
130 GOSUB 1000
140 PRINT "Normal + Border"
150 POKE L,PEEK(L) OR 2^6
160 GOSUB 1000
170 PRINT "Normal + Border + LC"
180 POKE L,PEEK(L) OR 2^6 OR 2^4
190 GOSUB 1000

210 PRINT "Reverse Normal"
220 POKE L,PEEK(L) OR 2^5
230 GOSUB 1000
240 PRINT "Reverse Normal + Border"
250 POKE L,PEEK(L) OR 2^5 OR 2^6
260 GOSUB 1000
270 PRINT "Reverse Normal + Border + LC"
280 POKE L,PEEK(L) OR 2^5 OR 2^6 OR 2^4
290 GOSUB 1000

310 PRINT "Alt Color"
320 POKE L,PEEK(L) OR 2^3
330 GOSUB 1000
340 PRINT "Alt Color + Border"
350 POKE L,PEEK(L) OR 2^3 OR 2^6
360 GOSUB 1000
370 PRINT "Alt Color + Border + LC"
380 POKE L,PEEK(L) OR 2^3 OR 2^6 OR 2^4
390 GOSUB 1000

410 PRINT "Reverse Alt Color"
420 POKE L,PEEK(L) OR 2^5 OR 2^3
430 GOSUB 1000
440 PRINT "Reverse Alt Color + Border"
450 POKE L,PEEK(L) OR 2^5 OR 2^3 OR 2^6
460 GOSUB 1000
470 PRINT "Reverse Alt Color + Border + LC"
480 POKE L,PEEK(L) OR 2^5 OR 2^3 OR 2^6 OR 2^4
490 GOSUB 1000

999 GOTO 999

1000 ' INKEY
1010 IF INKEY$="" THEN 1000

FILES command and memory in CoCo Disk BASIC


  • 2023-02-14 – See the comments from William Astle for more background on the memory usage.

Hello once again from the land of Disk BASIC. Today we look at another command: FILES

Device numbers in Color BASIC are hard-coded, meaning a specific number goes to a specific device. Here are some common device numbers:

  • #-2 – printer
  • #-1 – cassette
  • #0 – screen
  • #1 to #15 – disk

When Disk BASIC is added to the system, an extra 2K of memory is reserved for disk functionality. This memory is located directly after the 32-column screen memory (1024-1536) and it looks like this:

This is why a disk-based CoCo has less memory available for programs than a non-disk CoCo.

BASIC also reserves four pages of graphics memory (6144 bytes total) after this, which is why a BASIC program starts at 9729 in memory on a disk-based system. Memory locations 25 and 26 track where a BASIC program begins:

PRINT PEEK(25)*256+PEEK(26)

While you can use the PCLEAR command to increase reserved graphics memory to eight pages maximum (PCLEAR 8), you cannot get rid of all of them. Due to an oversight or bug in Extended BASIC, “PCLEAR 0” is not valid. You always have to have at least 1536 bytes set aside for graphics, even if you aren’t using them. (Thus why I think this is a bug.) Look at my PCLEAR 0 article for details on how to perform a PCLEAR 0 and get the most memory for BASIC.

For this article, however, I wanted to dive a bit in to how memory is being used for Disk BASIC. If BASIC starts at 9729, and graphics memory is 6144 bytes before it, and the 32-column screen ends at 1536, I should be able to figure out how much memory is reserved for Disk BASIC.

Subtracting the size of graphics memory from the top of BASIC tells me where graphics memory should begin:

PRINT 9729-1-6144

I subtract one there because the BASIC program starts at 9729, meaning the last byte of graphics memory is actually at 9728, one byte earlier.

And since I know the 32-column screen ends at 1535, bytes 1536 to 3135 should be for disk use:

PRINT 3584-1536

Disk Basic is using memory from 1536 to 3584, followed by the graphics memory starting at 3585.

As the FILES manual entry states, by default there are two disk buffers (devices) reserved – #1 and #2. If you need more, you use the FILES command. You can go all the way to FILES 15 and have the ability to open fifteen files at the same time!

If you have enough memory, that is.

On startup, a Disk BASIC system has 22823 bytes available for BASIC. That includes room for two disk devices. If you aren’t using them, you can type FILES 0 and get some extra memory for BASIC:

Above, you can subtract the “after” memory 23335 minus the “before” memory 23823 and get 512. Disk device #1 and #2 take up 512 bytes.

And this tells me that manual entry is possibly wrong since it says “If you do not use FILES, the computer reserves enough memory space for two buffers (Buffer 1 and 2), and reserves a total of 256 bytes for those buffers.” (emphasis mine)

If FILES 2 reserves 256, going to FILES 0 should have only increased memory by 256 — not 512. Shouldn’t it?

I decided to write a program to show the memory after each amount of FILES buffers. I quickly learned that when you use the FILES command, it erases variables. There are other things in BASIC that will erase variables, like PCLEAR. It looks like BASIC just clears out variables rather than relocate them if they change.

Since I could not use a variable, doing this with a FOR/NEXT loop was not possible. So, BRUTE FORCE FOR THE WIN!

170 GOTO 170

The semicolon at the end of the PRINT in line 160, and the endless loop GOTO on 170 just keep all fifteen lines on the screen without scrolling the top one off for the “OK” prompt. Running it gives me this:

Let’s look at that in text form, and I’ll highlight some odd entries:

  • FILES 0 – 22953
  • FILES 1 – 22953
  • FILES 2 – 22441
  • FILES 3 – 22441
  • FILES 4 – 21929
  • FILES 5 – 21417
  • FILES 6 – 21417
  • FILES 7 – 20905
  • FILES 8 – 20905
  • FILES 9 – 20393
  • FILES 10 – 20393
  • FILES 11 – 19881
  • FILES 12 – 19881
  • FILES 13 – 19369
  • FILES 14 – 19369
  • FILES 15 – 19369

The values immediately look odd, since memory doesn’t change between FILES 0 and FILES 1. But, FILES 0 seems to work. If you do that, you can’t OPEN “O”,#1,”FILES” without getting a ?DN ERROR (device number).

When FILES 2 happens, memory goes down by 512 bytes. FILES 3 doesn’t change anything, so it looks like it is allocating two buffers at a time, even if you just wanted one.

FILES 3 to FILES 4 goes down by 512 then FILES 4 to FILES 5 goes down by 512 as well.

That’s clearly not a pattern. In the above list, FILES 4 and FILES 13 to 15 stand out. For FILES 4, it jumps 512 for just that one addition device number, and for 13-15 they all report the same amount of memory.

I do not understand why. But, now that I know this, I can see you might as well use FILES 15 even if you only wanted FILES 13 because they take the same amount of memory.

Or do they?

Not-so-top secret FILES

There is a second option to FILES which is total size for how much memory is reserved for the buffers. But, it doesn’t work quite like I would expect. For example, if you do:

FILES 15,1000

…you should see 18215. 1000 bytes are being reserved for all fifteen buffers. But then if you do…

FILES 15,2000

…you might expect it to be 1000 less (17215) but on my system I see 17191 – 1024 bytes less. That’s 1K, so perhaps it’s just rounding to some multiple allocation size. We can test…

10 FILES 15,100:PRINT"FILES 15,100";MEM
20 FILES 15,200:PRINT"FILES 15,200";MEM
30 FILES 15,300:PRINT"FILES 15,300";MEM
40 FILES 15,400:PRINT"FILES 15,400";MEM
50 FILES 15,500:PRINT"FILES 15,500";MEM
60 FILES 15,600:PRINT"FILES 15,600";MEM
70 FILES 15,700:PRINT"FILES 15,700";MEM
80 FILES 15,800:PRINT"FILES 15,800";MEM
90 FILES 15,900:PRINT"FILES 15,900";MEM
100 FILES 15,1000:PRINT"FILES 15,1000";MEM
110 FILES 15,1100:PRINT"FILES 15,1100";MEM
120 FILES 15,1200:PRINT"FILES 15,1200";MEM
130 FILES 15,1300:PRINT"FILES 15,1300";MEM
140 FILES 15,1400:PRINT"FILES 15,1400";MEM
150 FILES 15,1500:PRINT"FILES 15,1500";MEM;
160 GOTO 160

That wonderful brute force program shows us something interesting:

Even though each allocation is only asking for 100 bytes more, we only see increases in multiples of 512. That must be the allocation size Disk BASIC is using. This may mean that when we are allocating more buffers, the actual space they need in not a multiple of 512 so that produces the odd increases the first example demonstrated.

Here is a simple program that demonstrates this:

10 MU=0:MA=0:SZ=512
50 MU=MU+A
60 GOTO 20

If you run this program, it starts off with 0 bytes used (MU) of an allocation of 0 (MA). It asks how much you want to allocate, and you can type in 100 bytes (A). If there isn’t enough memory allocated (MA) to fit another 100 bytes, it will add a new block of 512 (SZ) and continue.

I have not looked through the Disk BASIC manual to see if this is explained, nor have I looked at Disk Basic Unravelled, but I expect the answer is found in one of those.

For now, let’s just understand that using FILES with different values changes how much memory is reserved.

FILES this under…

With that understanding, the FILES command may be tricky to use efficiently in a complex program. Much like the CLEAR command reserving string space, knowing how much memory you need may take some thought if you don’t have enough free memory to just specify “a bunch.”

In a future article, I’d like to explore more about how these buffers are used, but in general it’s probably safe to assume that when we specify a record size for a direct access disk file:

OPEN "D",#1,"USERLOG",256

…and then use GET to read a record, that record has to go somewhere in memory. Above, with a record size of 256, I suspect we’d need at least a 256 byte buffer. Indeed, if you try to do this, you will get an ?OB ERROR (out of buffer space):

10 FILES 1,256
20 OPEN "D",#1,"USERLOG",300

But, changing the FILES command to specify 300 bytes for the buffer allows it to work:

10 FILES 1,300
20 OPEN "D",#1,"USERLOG",300

Oh. Maybe it is that easy to understand after all? And I suppose when we use multiple buffers (like reading from #1 and writing to #2), we’d need double that amount…

10 FILES 2,600
20 OPEN "D",#1,"OLDFILE",300
30 OPEN "D",#2,"NEWFILE",300

Indeed, FILES 2,600 works, but FILES 2,599 will show ?OB ERROR.

In a way, I’m surprised. I kind of expected the memory would be checked until you actually went to GET something, but I guess it pre-allocates.

This leaves me with another… What about FIELD? That command allows a record to be split up in to different fields and assigned to different variables. For example, if I wanted the 300 byte record to contain three 100 byte fields, I’d add:

10 FILES 2,600
20 OPEN "D",#1,"OLDFILE",300
25 FIELD #1,100 AS A$,100 AS B$,100 AS C$
30 OPEN "D",#2,"NEWFILE",300

…and this seems to work fine, so it doesn’t appear FIELD needs any extra space. (Note to self: look up how that works in the Unraveled disassembly book.)

I suppose this rabbit hole is pretty deep right now, so I’ll end this article by saying…

Until next time…

Avoiding DF and IE errors in CoCo BASIC


  • 2023-02-09 – In the comments, Lee pointed out a type in the first example, which has been fixed, and an error in the logic of the final routine which tries to write a partial line if it cannot write all of it. The original code tried to write using LEN() of the string, but needs to be adjusted to subtract one, since the PRINT will add one extra character (carriage return) at the end. The code has been corrected, and actually tested not. Thanks, Lee!

In Disk BASIC, you can open a file for input and read from it like this:

10 CLEAR 255
20 OPEN "I",#1,FILE.TXT"
50 GOTO 30

That would keep reading lines (data terminated by an ENTER) from the file and printing it to the screen until it reached the end of the file. It would then crash and report an Input Past End Of File error:


Avoiding IE ERROR

If we were using a fixed-format file, such as a configuration file, and we knew exactly what was expected to be in it, this wouldn’t be an issue. Suppose we had a file that just contained a Name, Address, City, State and Zip code. We could read just those entries like this:

80 CLOSE #1

For arbitrarily-sized files, like a text document from a word processor, you don’t know how many lines may be in it so that won’t work.

One solution is to write out how many lines are in the file as the first entry. For a file containing seven lines of text, it might look like this:


A routine to read this might look like:

10 INPUT "I",#1,"FILE.TXT"
30 FOR I=1 TO NL
70 CLOSE #1

This can also be used on cassette files just by changing the device number from #1 to #-1.


Disk BASIC provides the EOF function that will return the status of an open input file. It will return 0 as long as there is more data in the file, or -1 if the end of the file has been reached. That simplifies the reader code to look like this:

10 OPEN "I",#1,"FILE.TXT"
20 IF EOF(1)=-1 THEN 60
50 GOTO 20
60 CLOSE #1

And again, by changing the device from #1 to #-1 and EOF(1) to EOF(-1) it should work on cassettes. (Note to self: Does it? I haven’t actually tried this in almost forty years.)

When it comes to writing data, if you try to write more than the disk can hold, say, in an endless loop like this…

10 OPEN "O",#1,"FILE.TXT
30 GOTO 20

…you will eventually fill up the disk and get a Disk Full error:


Avoiding DF ERROR

Unfortunately, EOF only works on files opened for input and there is no similar command that tests output. And if there were, how would it work? If it just returned “yes, there is still room” versus “no, the disk is full”, what does “still room” mean? One byte left? What if you wanted to write two bytes?

Let’s pretend there is a Disk Is Full command called DIF:

10 OPEN "I",#1,"FILE.TXT"
20 IF DIF(1)=-1 THEN 50
40 GOTO 20
50 CLOSE #1

Above, if the disk still had room (even if it was just one byte), line 20 would return a 0 (“yep, there is still room”) and then we’d crash with a ?DF ERROR in the next line, since we tried to write more than that one byte left.

What we really need is a command that tells us how many more bytes of data we can write to the disk, and we don’t have that.

But we can make something close. Close-ish.

New features for FREE

Disk BASIC has the FREE command which returns the number of free granules on a specified drive. There are 68 granules available on an empty disk. If you know what a granule is, that means something, but all I really knew back then was the largest program I’d ever seen was “13 grans”…

TL:DNR – A granule is 2304 bytes.

A CoCo RS-DOS (what we called Disk BASIC) disk is made up of thirty-five (35) tracks, each holding eighteen (18) 256-byte sectors. That is 161,280 bytes of storage (35 * 18 * 256), which really seemed like a bunch in the early 1980s! Track seventeen (17) is used for the disk directory and file allocation table (FAT), so there is really only 34 tracks you can use for programs or data storage which gives you 156,672 bytes (34 * 18 * 256).

For reasons I do not know, each track was divided up in to two (2) granules. That makes a granule represent nine (9) sectors. Therefore, a granule is 2304 bytes. (9 * 256 = 2304).

When FREE(0) returns 68 for an empty disk, that is 156,672 bytes free. You can multiply the value FREE returns by 2304 to see how many bytes are free.


PRINT FREE(0)*2304

Using FREE we could determine if there was still room on the disk for writing more data, as suggested by Hawksoft’s Chris Hawks on the CoCo mailing list:

On first glance, it seems like you could just check for FREE(0) to be 0, and stop writing when it is:

10 OPEN "I",#1,"FILE.TXT"
20 IF FREE(0)=0 THEN 50
40 GOTO 20
50 CLOSE #1

However, once a new file is created (even if you have written nothing to it yet), the number of free granules goes down by one since there is no longer a full granule available. This can be demonstrated using a program like this:

40 PRINT #1,"A";
70 CLOSE #1

Running that on an empty disk should print the following:


Above, initially there were 68 granules available, then we opened/created a new file and then there were 67 granules used. We wrote one byte (“A”) to that new file and there were still 67 unused granules. We are creating a file an allocating that 68th granule for it, whether we write any data to it or not. After writing one byte to that granule, we can not tell how much room is left in it. This means if you had started with only ONE free granule and ran this program, you would see:


The moment the file was opened, a granule was consumed for this new file, leaving zero granules available. Thus, the check for FREE(0) being zero would immediately be satisfied, and even though you had bytes available to use in that granule, the program would skip writing because FREE was returning zero…

This approach simple won’t work if there is only one granule left (a file would be created, then no data would be allowed to be written). And, if there was more than one granule left, it would be wasteful since even writing one byte in to the new granule would count as “full” and the rest of those 2304 bytes would be wasted.

Side Note: Since the smallest amount of allocated space you can have is one granule, a file of one byte consumed 2304 bytes on the disk, just as a file of 2304 bytes would. MS-DOS and other file systems have the same issue with their allocation sizes.

Code compensation

Disk BASIC has no solution for us, by we can easily come up with our own. As long as we control what is being written, we should be able to determine the size of what is being written and do something like this:

0 '
3 '
10 DF=FREE(0)*2304:DU=0
50 GOTO 30
60 CLOSE #1
999 END
1000 ' WRITE A$ TO BUFFER #1
1010 DU=DU+LEN(A$)+1

The idea behind this code is that a subroutine is used to write whatever is in A$ to the disk file. That subroutine will keep track of how many bytes were written. In line 10, DF is initially set to the number of free bytes by taking FREE(0) and multiplying it by the size of a granule (2304 bytes). DU is how much of that has been used, so it starts out at 0.

In the main loop, as long as DU is less than DF, the subroutine can be called to (attempt to) write A$ to the disk file.

In the subroutine at line 1000, DU is incremented by the length of A$, and 1 is added since PRINT will add an ENTER character to the end of the line. If DU is less than DF, the line is actually written to the file. Otherwise, it is skipped.

This could be made more elegant, but it’s a simple approach that should work just fine.

Code compensation, improved

Some improvements might be to make it not just give up if all the data won’t fit, but to write as much as possibly leaving disk free at zero at the end. This could be done like this:

1000 ' WRITE A$ TO BUFFER #1
1010 LN=LEN(A$)+1
1030 IF LN>0 THEN PRINT #1,LEFT$(A$,LN-1)
1040 DU=DU+LN

…or something like that. The idea is if there is only ten bytes left, and you try to write 15, it will just right the first ten bytes of that string and then RETURN. This, too, could be made more elegant.

How would you improve it? Leave your suggestions in the comments…

Sadly, there’s not much you can do when using a cassette since there is no way to know how much tape is left.

Until next time…