Writing a Utility Program for the ZX81 Was Such Fun


Here's a quick program I wrote to turn Sinclair BASIC into assembly code.

Sometimes, you do things out of necessity. I spent an inordinate amount of time working on adding automatic scrolling to a PRINT command. In fact, I haver another version of my print scroll routine I’ll talk about later. But this is about a utility program. A way to print out BASIC code in hex. And this article is about what I did and why.

# Let’s start with the problem.

While working on print scroll, I wrote the assembly program like I always did. What that means is I use the z80asm linker to build a working ZX81 program. The program usually has two lines. The first is a REM with all the machine code. The second is a BASIC routine that runs that code. This is the first version of print scroll I wrote to give you an idea of what it looks like.

   1 REM /-**PRTSCRL**SLR/2024**
  2 RAND USR VAL "16514"

Note that the first line actually has a bunch more code in it but I add a newline (hex $76) at the end of the first line to hide it. The /- at the start is actually a jump into the code. The 2nd line just runs it—and you don’t usually see it unless you list it because of the aforementioned newline.

Now this works great for most of my programs because this is exactly what I want them to do: load and run. This is also nice for my visitors because they don’t need to understand the ZX81 and how to run programs on it. I can point to an emulator with the file and off they go.

So what’s the problem you might ask? Well, kind reader, it isn’t want I want to do in this case. The routine I wrote doesn’t actually do anything on its own. Instead, I need some BASIC code to make the program work.

# Making my assembly code work like BASIC.

Now, while testing, I let the program run and then exit with an error. I could then add in some BASIC lines to test how the routine. Although this worked, it made debugging slower. Each time I had to recompile the program, I had to load in the new routine and retype the BASIC code. Not very efficient.

Now, I’d known I could map out the whole ZX81 memory and add in the BASIC code into my program, but the templates I had were for other compliers. Since I had a method that worked, I ignored those templates. Now, however, I was regretting that after what seemed like the tenth time entering in the same code. I needed to make a change.

Although I’d like to say my new found motivation would force me to dig into my ZX81 manuals and create my own template. it didn’t. This is the Internet age. As anyone today would, I started by searching. As luck would have it, someone else had done this already.

# Taking a slight detour.

Now, of course, I didn’t get there right away. I first stumbled onto Bumbershoot Software’s blog and his article on writing ZX81 machine language programs. I’d read the article before, but this time was paying closer attention as he was using the same complier that I was.

Now, that article didn’t quite get me to where I wanted, but I kept reading. His next article was a different take on how to save machine code on the ZX81 by placing it later in the program. In that program was a link to his custom linker for that. Enjoying this side quest, I followed it.

Although I wasn’t looking for a custom linker, to my surprise there was another bit of code there that I did care about. A wonderful little program called Wallpaper. I actually remember the program it was based on having owned the book it came from. But it was the code that got me excited.

The program was a mix of machine code and BASIC, which was exactly what I was trying to do. And, even better, it used z80asm to do it! I now had a template to base my code on. A few tweaks later, I had a working program. However, I was still missing something—my custom BASIC code.

# Using the ZX81 to write machine code from BASIC.

Before digging into the approach, let’s talk about what I needed. Adding BASIC into machine code means breaking down the tokens and entering them into the code. For example, here is the code for the CLS in line 10.

        ; 10 CLS
  db $00,$0a,$02,$00,$fb,$76

The first two bytes represent the line number in hex ($000A or 10). The two bytes are the length of the line (including the newline). The text of the line, which is the single token for CLS ($FB). The line ends with a newline ($76). This line is pretty easy to build by hand, but BASIC always that simple.

ML Basic, Print Scroll 2 Output, ZX81 Screenshot, 2024 by Steven ReidML Basic, Print Scroll 2 Output, ZX81 Screenshot, 2024 by Steven Reid

A more complex example, is the GOTO in line 50. What makes this more complex is how the ZX81 represents the number 30. In this case, Let's first start with the code:

        ; 50 GOTO 30
  db $00,$32,$0a,$00,$ec,$1f,$1c,$7e
  db $85,$70,$00,$00,$00,$76

This probably looks long to anyone not used to Sinclair BASIC. Numbers are stored in the ZX81’s character format ($1F is 3 and $1C is 0), followed by the number designator ($7e), then the five byte binary form of the number. Thus, it takes 8 bytes to represent the number 30. Whew! And we have more complex stuff still to do.

# I have an easier and faster way.

Now, my first thought was to find a Sinclair BASIC complier. JSZeddy has one, but it isn’t easy to parse out the hex and I wanted something pretty straight forward. Since the BASIC code is stored in memory in the format I needed, I decided to just use a ZX81 emulator.

The program I wrote is straight forward, as you can see from the code. But let’s break it down. The first part of the program is the BASIC code I want to enter. To avoid that code from running, the first line skips to the machine language lister at line 100. Note this works well because I use line 1 for the program name and most of my programs start at line 10. The BASIC code I want to list is after that. Line 99 is a STOP command that I’ll look for later.

ML Basic, Print Scroll 2 Program, ZX81 Screenshot, 2024 by Steven ReidML Basic, Print Scroll 2 Program, ZX81 Screenshot, 2024 by Steven Reid

To print the actual code, I first set a program counter with the address I want to start at. BASIC starts at address 16509. Since I don’t need to print the first line, I add 15 bytes to jump over that line. I also set the variable H$ which holds the hex numbers. More on that in a minute.

The loop starts at line 120 by setting P to the value at PC, which is then printed using the routine at line 200. Now, I check for a couple of things before continuing. The first is if we’ve hit a newline—character 118. If so, I jump to a routine that pauses the display so I can copy the codes. When done, I can hit a key to clear the screen and the next line is printed.

The program then checked if it reaches the STOP token (character 227). That ends the program, since we don’t need to continue into the program’s code. If it doesn’t, it increments the program counter PC and repeats the loop. A pretty straight forward process.

# How about that hex routine?

I brushed over this, but I want to talk about that routine for a second. My first version of the program just printed the decimal representation of the byte. That is because PEEK returns a regular number. Although I could have just entered that number in my code, I wanted to use hexadecimal numbers as they format better. What I needed was a converter.

ML Basic, Print Scroll 2 Listing, ZX81 Screenshot, 2024 by Steven ReidML Basic, Print Scroll 2 Listing, ZX81 Screenshot, 2024 by Steven Reid

I didn’t remember ever printing hex before, but the solutions was quite simple. I just calculated the two digits with some simple math. Then, using that H$ string from earlier, I printed the hex digit based on the index of the decimal number. Done!

# Thinking about the future of the routine.

I’m quite proud of this little program. I don’t write many utilities programs on the ZX81 and this one was fun to figure it. I remember reading all those years ago as a teenager about how BASIC lines were structured in memory. Back then, I didn’t know what to do with that information. Forty years later, I found a use for it. Plus, I love that little hexadecimal routine.

One advantage of using the ZX81 for creating the BASIC code is that I know that it will work. The ZX81 does all the syntax checking and number conversions. Because of that, I can trust the code I’m typing into my programs will work.

However, it isn’t the smoothest of work flows. Typing in hex is tedious and it can be difficult to follow the numbers. My mind kept remembering entering in those C64 programs from magazines back in the day. As such, it would be nice to get there a different way.

One way of getting there would be to write a program, save it, and then modify my program lister to basically do what this program. I could then copy and past the hex directly into my program. Or, even better, save as an include file I could link in. This saves the step of typing in the code but has the drawback of needing to save the program each time it changes.

Although I think that would work, I don’t know how many other programs I’ll write that will need Sinclair BASIC mixed with machine code. I’ve written quite a few programs to date without needing it. Having options is nice, though. Perhaps this will be the way I write all my future programs.



Comments on this article:

No comments so far.

Write a comment:

Type The Letters You See.
[captcha image][captcha image][captcha image][captcha image][captcha image][captcha image]
not case sensitive