Lab 1 — Moving Character (Commodore 64)

This blog is dedicated to preserving my experience in programming retro "home" personal computers. I will use the form of lab notes.
Source material: this book. This is a typical "type-in" game book aimed at children. I had quite many such books in my childhood and can confirm their usefulness in learning programming on home systems. They provide interesting examples and give room for improvement

Lab 1 is simple: setup a working Commodore 64 programming environment and display a character that moves across the screen. It is almost completely described in this page:
Book describes the inner working of such animation. It would have been dead simple to make it work back in the day, but today it takes some preparation and learning some things about these computers peculiarities. Read below for complete information.

First we'll need to setup the emulation. I used Hoxs64 emulator.
Download, install and run. No special actions needed to get it running C64 Basic.

Next, we need keyboard layout for reference (so we'll know which keys to press to get characters we need):
Book uses C64 control characters without explaining their meaning, so we'll also need this reference table.

We also should learn how to load and save our progress to virtual drives. Tape loading is as simple as issuing LOAD or SAVE commands, but I've used tapes back in the Spectrum days, and don't want to return to them ever again. I hate floppies too, but not as much as cassette tape. Disk drive commands on C64 are somewhat complicated. You must issue
OPEN 1, 8, 15, "N:DISKNAME,ID" 
to format drive, for Christ sake. It is magic incantation, not a command for everyday task. Such was the life with these machines, most of the time they were as simple as they get, but every now and then a bit of voodoo was necessary to make them do things. Saving and loading is less complicated, though: SAVE “FILENAME”,8 to save, SAVE “@0:”FILENAME”,8 to overwrite, LOAD “FILENAME”,8 to load.

Now let's get to entering the code and running it. And more importantly, understanding what it does.
First things first. Many old home computers, including C64, used a simple but powerful screen editor. You can move cursor in any direction and type everywhere on the screen, overwriting what was there before. When you press Enter, current line is evaluated. If it starts with a line number, this line is stored in the program memory, otherwise it is executed as a BASIC statement.

Now, THIS is a programming environment, not those fancy-pants IDEs and REPLs of modern languages. This is DEAD simple. If you see the line on screen, you can edit and execute it. No need to recompile anything. If you need to compute something, you just type it and press Enter. If you need to make corrections, you just type new code over the old. You don't press some special keystroke to get to previously typed lines, you move your cursor and type. That is what makes programming this thing like playing and tinkering — the feeling you don't get with most current programming languages. Python is the closest thing, not perfect, but fine. It lacks the rich simplicity of those early BASICs, you'll need to import modules and install libraries to do anything non-trivial.

But I digress. Let's go over the code. I won't detail common BASIC operators, they are like riding a bike to me: I haven't used them for years, but as soon as I see them, knowledge returns. First line features control characters. Inverse heart symbol is clear screen (Ctrl-Home), inverse Q moves caret down one line (down arrow). Commodore screen is 40x25:
So we should check loop limits in second line, they look somewhat strange. Third line has some interesting things. First, note the lack of spacing between PRINT operator and its operands. No concatenation operators needed either. Surely, you can enter spaces there, but they were often omitted to save precious memory. Printing in this dialect of BASIC always starts on new line. SPC(I) operand moves caret right I times, and hollow square moves it up one line (up arrow).

Fourth line is delay loop, and 200 is simply a magic constant. 200 empty loops take about a ⅓ of a second on my emulator, which claims to be cycle-accurate. These delay loops are abomination of any old computer program when running it on newer machines. It is not easy to replace them with proper timed delays, so they were necessary evil. Also, even good old C64 is quite fast — it can run 10 empty BASIC loops during one screen update at 60 fps — and possibly do much more using raw machine code.

Enough theory, let's practice! Typing the program in...
...we now can RUN it. And sure enough, there's our moving X mark:
And we can adjust its speed by changing the delay in line 40. But wait, there is a slight problem: X moves from second column to the last, not from edge to edge! This is understandable, because we always print at least one space character before drawing X. How do we deal with it? A naive approach is to change loop index from 1 to -1 in line 20:
Of course, it doesn't work and gives a helpful error message: SPC does not work with negative offsets. Let's just print X in first column before doing the movement loop. We still have space for nine additional statements between line 10 and line 20, so let's enter:
15 PRINT "X"
And it (apparently) kind of works, but we can't see anything, as it's overwritten instantly. Let's move the delay to the beginning of the loop: change line number 40 to line number 25. Note that if we issue LIST command after that, line 40 is still around:
To get rid of it, just enter 40 on the empty line and press Enter. Yay, now it works as intended!

Let's save it to disk. Insert a blank one from Hoxs menu (it even formats it for you) and ask it to write the program to it:
SAVE "MOVEX", 8
Okay. Now the task with a star: print a moving line. It's pretty straightforward: replace X with intended line (for example, HELLO!) in lines 15 and 30, and change loop limit from 38 to 33 (as we print 5 more characters).
Note that it's still enough to erase one space, because the rest will be overwritten by text itself. Let's save it too:
SAVE "MOVELN", 8
Don't forget to save floppy image to file from Hoxs menu, it doesn't do that automatically.

I think that's enough for today. It was pretty pleasant experience. It's trivial to reproduce the result in some modern programming language. You'll just have to replace control characters with ANSI sequences (ESC[B for cursor down, ESC[A for cursor up, ESC[2J for clear screen). There's one catch: terminal can be of any width. So we'll need to call an OS function to get that value. We also should replace empty delay loop with appropriate sleep command. And your terminal must support ANSI.

import os
import time

print('\033[2J\033[B\033[B\033[B')
print('X')
for i in range(1,os.get_terminal_size().columns-2):
 time.sleep(0.1)
 print('\033[A', ' '*i, 'X')

There is, however, one important difference: its orders of magnitude more complicated than just issuing simple statements and executing them. But such is programming these days :(
I'll be back sometime in future with another example from this book.

Комментарии