NEWS, EDITORIALS, REFERENCE
BASIC Wedge Programs
Following up on my technical deep dive, Anatomy of a Koala Viewer, I've been thinking about how one could go about making improvements to the viewer and its UI, or lack thereof.
My thinking about this is completely aside from work on C64 OS, except that everything one learns helps in developing other projects. And I am a C64 user after all, I'm not going to just live the rest of my life inside the bubble (an OS environment) I'm creating for myself.
I was thinking about how command line interfaces normally work. Usually the shell program offers a single line editor. And whatever you type into that line, when you press return, is parsed and interpreted by the shell. Typically the first word is the name of a program and subsequent words and symbols are meant to be arguments given to the program. The shell has a path environment variable that defines a set of file system paths to be searched through when looking for the program.
Presuming that the program is found, it is loaded and run automatically and it has programmatic access to all the arguments. Shell's do get more sophisticated but we don't need to worry about that for now.1
Think about how the "command line interface" works on a C64. It is actually the BASIC interpreter. You are free to start entering a program, simply by putting a line number at the start of a line you type out. But all immediate mode lines, those lines you enter without a number at the beginning, are constrained to a limited set of BASIC commands. BASIC 2.0 has 71 commands, but only a subset of those can be used in immediate mode.
How do you load a program? You use the LOAD command. And it takes a fixed set of arguments, "filename" (this argument can include a partition number and directory path), then a device number and a relocate-supression flag. Although all of these arguments can be omitted depending on whether the defaults match what you're after. (LOAD by itself will start the computer loading the first file it finds on the tape in the datasette.)
There are extensions to this command set that can be made, both with software and hardware. For example, when you're using an IDE64, you can directly issue "CD" and "MKDIR" and so on. That's because the hardware adds those commands to the BASIC language. JiffyDOS does the same thing to add its commands, * for transferring files, @ to read the current device's command channel, / to load relocated, etc. But none of these extensions allow you to pass arguments directly to a program that was just loaded.
BASIC Wedge Programs
One reason you'd want to add an argument to a program call is the simple use-case of being able to pass a filename to that Koala Viewer. All the viewer does is show a Koala image, but you have to manually load the data yourself. And you have to load it using the correct flags, or the correct JiffyDOS load commmand, so it goes to the right place in memory. Then you run the program and it assumes you've put the correct format of data into the correct place in memory. Otherwise, you just see crap on the screen.
It works, but its as brutal as you can get.
There are lots of multi-color image file formats, as outlined in that post. They are all very similar, but different enough that the viewer program needs to know which ranges of memory hold the color data, and the bitmap data, etc. The Koala Viewer program, however, has no way of knowing what data was loaded in, (or even if any data was loaded in) because it has no concept of files. The file loading is handled entirely by BASIC and JiffyDOS, which just puts raw data into memory. That Koala Viewer in fact takes no input whatsoever.
I have a different idea.
How about this. You load and run a program, let's call it "image viewer". You do this the standard way. If you have JiffyDOS £ IMAGE VIEWER. Or if it's off in some other place then maybe like this:
That sort of thing is pretty common to load a one file program, from a CMD device for example.
This command loads the program into memory according to its header address, so maybe it's assembled to $C000 or somewhere else convenient. And it's jumped to immediately to run it. The program has put assembly language routines into memory, and it has been run, which wedges the BASIC vectors to add new BASIC commands, but it doesn't need to implement a UI. It doesn't need to show you a full screen menu, or anything like that. It just needs to add one or more commands to BASIC.
BASIC already provides a means of inputting lines, and your code has the ability to intercept the interpretation of a BASIC line. I am going to reserve the technical details of impelemntation for a later post, when I've actually got the tool written. But the short explanation is that the ability to wedge BASIC is done by redirecting a series of vectors to your program's own routines.
By simply returning to BASIC, you continue to use BASIC (and JiffyDOS if you have it) to navigate around the file systems of the devices you have hooked up, and to view directories the way you always would from BASIC, such as by pressing F1 or with @$. When you want to view an image file, you would use a new BASIC command that the loaded image viewer has wedged in. In this case, maybe just the keyword "view" would be suitable:
Besides being able to just use BASIC to navigate your drives and their file systems, the advantage here is that the program gets to see the filename. It can load the data in, according to how the data should be loaded, even if that still means loading it directly to where the file itself wants to go (as odd and archaic as that seems in the context of a modern OS.) But, because it sees that file extension, one image viewer program can handle multiple formats, like Advanced Art Studio, Amica Paint, CDU-Paint, Draz Paint, Koala etc. Or it can even handle other formats besides multi-color.
Presuming these files have standardized, identifiable elements to the file name. As long as the type is identifiable from a feature of the name, the program can look for that feature to determine the file type.
One complaint about command line programs is their inherent lack of visible UI. Sometimes it can be hard to remember how to use them. You load and run an image viewer as I'm describing, and instead of getting a menu or an input prompt, you get nothing. You're just back to the regular READY prompt.
Two solutions to this come to mind. The first is to immediately print out a simplified help text. Even if it's just a one-liner like:
USAGE: VIEW "IMAGEFILE"
That would get you pretty far down the line to knowing how to use it. The second thing to do would be to have the program wedge at a minimum two commands. The main command to do whatever the program is designed to do (i.e. "view") and also a standard "help". If every program that used this model implemented the "help" command, you could just type help whenever to reveal A) if such a program is loaded in. A syntax error will tell you that the program hasn't been loaded yet. And B) It can be used at any time to remind yourself of the commands and any extra arguments or syntax. And the explicit invocation of help could also be used to show extended help, beyond what the initial one-liner showed.
How about exiting? What do you do to "exit" this type of BASIC wedge program? Well, to me there are two kinds of exit to consider. The first kind is how to get back to the READY prompt after something the image viewer takes you to a full screen bitmap. Even though the Koala Viewer accepted slapping the space bar, somehow I managed to not notice that for quite a while. I tried pressing many different keys and then just gave up assuming there was no way to exit.
In after thought, the space bar is the obvious key, but in my opinion pressing the run/stop key should always work. It is after all the "STOP" key, Commodore's equivalent of Escape. "RUN" is supposed to be the function of the key when pressed in conjunction with SHIFT. But on further thought, why shouldn't any key be pressable to return to the READY prompt?
The second kind of exit is, how to unwedge the program from BASIC? Or, how to move on from the image viewer to a different kind of program? For the most part, you don't really have to explicitly unload such a program. Pressing run/stop-restore will probably reset the BASIC vectors and thus unwedge the commands. And otherwise, the program can remain resident.
If you load a different kind of BASIC wedge program, it may well overwrite the all or part of the first one, but then it'll update the BASIC vectors to point to its own routines. And there was no explicit need to get rid of the first one.
UPDATE: November 27, 2017
Pressing run/stop-restore does not reset the BASIC vectors. I tested that last night. However, doing a jump to $E453 (SYS 58451) will run the KERNAL's routine that restores the BASIC vectors to their hardware defaults. This is better than manually setting them, because different KERNAL rom's have different vector defaults. For example, JiffyDOS points two of those vectors to its own routines. One could do this SYS manually to unwedge an installed program.
But here's what I'd suggest. When a BASIC wedge program is first run, the first thing it should do is JSR $E453 to reset the vectors, this will unwedge any previously installed program. Then, it should copy the vectors to its own variables and change the vectors to point to its own routines. But, in its routines, when forwarding control to BASIC, forward through the backed up vectors and not to hardcoded addresses. This will maintain support for the special features of alternative ROMs like JiffyDOS and its command wedge.
In my mind, this program model of wedging in some BASIC commands works really well for the Koala Viewer. It's a simple one-purpose program, that takes at least one argument. I can immediately think of several other programs that fit the same description that could benefit from the same paradigm.
How about an unzip utility? As PC/Mac command line unzip/gunzip shows you, you really don't need a full UI. Just load in the tool, it wedges in a command "unzip". (And help.) You navigate to a directory where a .zip/.gz file is. And enter:
It uses the .gz or .zip extension to know what kind of archive it is. It just immediately starts unzipping the contents and writing them to the same output device and path as the original file. As it finds files in the archive, it can print out the name of the file. When it's done, you're just back at the READY prompt, and can immediately call the command again with a different filename.
How about for a background SID file player? Load in the program, and it wedges three commands, PLAY,PAUSE and HELP. The play command can take two arguments, filename and maximum number of seconds to play back. 0, could mean to play to the natural end of the song (presuming the natural end can be found.)
That loads in the file, maybe it tells you where in memory it was loaded to, wedges in to the IRQ the necessary routine to play the file, but ultimately returns you to the READY prompt with the file still playing. Want to stop the music?
This command could simply unwedge the IRQ routine. Would it be compatible with every SID file? No, it would probably conflict with some of them. But on the whole it would be pretty cool.
There are side benefits to wedging these sorts of programs into BASIC. Want to make a playlist? Want to make a slide show? Just write a BASIC program that loops reading file names out of data statements, and calls the custom command. You have to load the wedged program in before loading in the BASIC program, but it'd be like this:
10 FOR I=0TO2 20 READ A$:PLAY A$,180,1:NEXT 30 END 40 DATA "SONG1.SID","SONG2.SID","SONG3.SID"
Oops, there's a gotcha here. When inside a program like this that loops, you'd want the PLAY command not to return until the song is finished. That's easy to handle. The PLAY command can take an optional third parameter. That's what the extra ,1 does after the number of seconds of playback.
You could do the same thing with an image viewer. Let BASIC work as a script language to move from image to image.
10 FOR I=0TO2 20 READ A$:VIEW A$:NEXT 30 END 40 DATA "IMAGE.KOA","IMAGE.HED","IMAGE.OCP"
Run it, you're looking at the first image, slap the long one, and boom, the next one loads in, etc.
I think it's a great idea. And to that end, I've been reading Advanced Machine Language for the Commodore 64 by Abacus Software. It devotes the whole 3rd section of the book to hacking/wedging BASIC with your custom machine language programs.
I've learned a lot so far, and a few neat things about BASIC that I never knew before. So when I get a bit further into it, I'll write another post describing my findings, and how it works out.
- Shells get more complicated because the output from that program can be streamed into either a file or as the input of another program. That's what makes a unix command line so powerful. [↩]