NEWS, EDITORIALS, REFERENCE

Subscribe to C64OS.com with your favorite RSS Reader
October 15, 2020#105 Software

Load and Run from 6502 ASM (2/2)

Post Archive Icon

Originally I thought I'd pack this into one post. I decided to split it into two so we could get into some details about the power up process both in hardware and software, and then have room in this post to get into the details about how to deal with the variations and complexities—that have crept in over the years—for what was once a simple task, loading and running a program on a C64.

If you've just stumbled upon this post, I encourage you to read part 1, Load and Run from 6502 ASM (1/2), first. Here's a very brief summary of what was covered in part 1:

It used to be very straightforward to load and run a program on a C64. Most users had only one disk drive, on device 8, which was the drive's default. The load instruction was one command that would frequently appear on the disk label itself or in the first page of the software package's manual. Early software, i.e. most software, came to rely on being booted from device 8, even as users purchased and hooked up additional and more sophisticated storage devices.

C64 programs assume they own the whole machine, and often depend on the machine being configured as it would be following a fresh power on. When the machine is powered up it goes through an initial reset cycle, which runs 4 standard KERNAL routines before passing control over to BASIC. From BASIC there are several ways that a program can be started, depending on how it was written and whether the machine has a stock or third party KERNAL ROM like JiffyDOS. The sad fact is that there is no one single reliable command that will guarantee the program will be started correctly.

 

An Overvew of the Options

If you only have one drive, and it's a 1541, then it's on device 8. The disk might even specify the load command you need to issue. But if it doesn't, then typing…

load "*",8,1

…is almost certainly a safe bet. Heck it might even auto-run from that command. But if it doesn't, just type RUN when the load is complete.

But over time, what started out pretty simple has ballooned into a plethora of possible options, making the simple process of starting a program unreliable. Here are the most important questions that need to be answered:

  1. Does the program run from a device number other than 8?
  2. Does the program load to its 2-byte header address?
  3. Does the program begin by being run as a BASIC program?
  4. Does the program require a physical 1541? 1
  5. Does the program require a 1541 track and sector layout? 2
  6. Does the program span multiple disk images?
  7. Does the program hijack the IEC protocol thus requiring its drive be alone on the bus?
  8. Does the program require the stock KERNAL ROM?
  9. Does the program run on a PAL machine, NTSC or both?

In addition to these questions, there are at least a few meta questions. Some are applicable only when you've installed the software on a more modern storage device.

  1. What device # is the program installed on?
  2. What partition # on the device is the program installed in?
  3. What is the directory path in the partition to the program?
  4. What is the name of the file that must be loaded first?
  5. What port is the primary controller expected to be in?

Consider #4, above, the name of the file to load first. A program might consist of 2 or 3 or more files, all of which are file type PRG. When these files were originally on a 1541 disk the boot file would be first in the directory so that the file pattern "*" would match it first. But when several such programs are put into the same directory, the directory can only have one file that comes first. Thus it becomes important to know what file is the one used to start a given program. The other files could just be data, for levels or high scores, or whatever.

There might be other questions. But these are the ones that have come to me first.


The Amiga and WHDLoad

The Commodore 64 is a simpler machine than the Amiga, but it still has a wide range of possible hardware configurations, and software needs can be quite varied. The Amiga, being a more advanced system, has a whole other range of complexities, but it is not entirely dissimilar from the C64.

For example, on an Amiga, some programs depend on features found only in specific versions of Kickstart. Some games expect to be loaded from a 3.5" floppy disk, not from a drawer on a hard drive. Some games are perfectly okay running (multi-tasking) side-by-side with Workbench, while other games expect that the machine was booted directly into the game and that Workbench is not present and stealing any resources, like memory. Sometimes a game will work only with features found in the original chipset, and not the newer chipsets. And I'm sure there are other even more technical issues, such as how much fast memory is available, whether an FPU is available, which 68K CPU variant is available, how fast the CPU is clocked, etc.

The details of the compatibility problems are different, but the general situation sounds strikingly similar to the C64, especially if the software has been written to expect a specific hardware configuration.

Starting back in the 1990s a program called WHDLoad was developed for the Amiga. The "HD" undoubtedly stands for Hard Disk, the first purpose being to facilitate running software from a hard disk that was designed to run from floppies. The "W" has a more dubious meaning. Some suggested it comes from the name of the program's creator, Webl. Others have suggested that it means "Workbench" since it runs from Workbench and lets you launch into games from within Workbench. I like this latter theory, but the former may well be right.

Screenshot of WHDLoad. An Amiga 1200.

You can read more about WHDLoad on Wikipedia or on its official website. WHDLoad does not distribute the games themselves, as these are often still protected by copyright. The games must be acquired, legally, and the contents of the original disks must be stored in Amiga disk image files, which are organized in drawers on a hard drive. Each game is then paired with a custom WHDLoad slave interface. You launch the game via the icon associated with the slave interface, which starts the WHDLoad program first. This reads the information from the slave interface and proceeds by reconfiguring the Amiga in any conceivable way necessary to allow the game to run properly.

This can involve moving Workbench out of main memory and up into some higher area of memory that the game doesn't touch, and disabling multi-tasking by reassigning IRQs, changing Kickstart versions, etc. The details about everything WHDLoad is capable of changing, and exactly how it does this, is beyond my current understanding of the Amiga. Importantly, it is easy to use and each game has a slave interface that provides WHDLoad with the information necessary to reliably run that game.

In short, the modern C64 user could really use something akin to WHDLoad. So let's talk about that.

My own history from there to here

I started on a VIC-20, and I actually came quite late to the C64. My first C64 was a C64c which my parents bought me for Christmas in 1990, when I was 9. It came with a 1541-II disk drive, an Epyx Fastload cartridge, and a case of around 60 disks. The disks were a mix of commercial productivity software, commercial games, and some demos and pirated games. There were also a few disk-based magazines, such as Compute's Gazette and LoadStar. And, of course, GEOS, with which I was totally enthralled as a teenager.

Given that I had only a single 1541 disk drive, on device 8, loading programs was mostly as easy as the easiest case I described. But there were some complications even then. For example, some programs refused to work with the Epyx Fastload enabled. I had to discover which these were through experimentation. My sole source of C64 software for a few years came from that case of disks, so, I thoroughly explored each one and tried out everything I had.

On the disks containing cracked or pirated games, it was very common that a single disk would contain 3, 4, 5 or more games. With many of these it was a crapshoot exactly which file was the one that launched the game. The files from multiple games were mixed and flowed together in a single directory. And for many programs I simply had to remember any special tricks I'd discovered through trial and error.

I didn't even know about NTSC/PAL issues, because everything I had was NTSC compatible. But within a few years I acquired a modem, discovered a couple of BBSes with C64 software, and expanded beyond my case of disks. I soon discovered that some software, no matter how it was loaded, would always crash in a rather flamboyant display of color, broken graphics and sound. PAL-only programs don't come with warnings. You don't get a standard message that says, "You are on an NTSC Commodore 64, and this program requires a PAL Commodore 64." You have to infer that on your own, by realizing that the programmer never intended for the program to be this broken.

In short, Commodore 64 programs don't come with metadata that defines their special requirements. Nor is there a standardized intermediate program launcher than analyzes those requirements and checks to see if your computer does or can meet them. Each comes with nothing but the raw data. Some metadata can be found in the user manual, and nowadays some metadata is available online. But other metadata, often the most important, is not available at all, and it's not available where it's arguably most useful, which is on a Commodore 64.

Caudron game metadata on c64-wiki.com.
Metadata for the game Cauldron on c64-wiki.com

The above is useful in some cases, such as for emulator users mostly. But about the only information useful to a real C64 is that it uses a controller in port 2. Even that screenshot, which was originally designed to display perfectly on a C64, is here an animated .GIF or a .JPG, which ironically is either very difficult or impossible to display on a C64. The game's original media was tape or disk, fine, but it doesn't indicate if you can copy the disk to a hard drive, or even run the .D64 from an SD2IEC. It doesn't say if it'll crap out on an NTSC machine, or whether you'll get a red blinky LED on device 8 when you try to run it from device 9, and so on.

When I acquired a CMD HD, naturally, I started loading games from my floppy disks into partitions on the hard drive. But then all the new problems I described in part 1 started to pop up. Here's how I dealt with these issues:

PAL-Only

I didn't own a PAL machine, so if I downloaded a game or demo and found that it doesn't run on NTSC hardware, I would be disappointed but then I would just delete it. That solves the problem of having to remember which is which.

CMD HD Partitions

I would load a game into a native mode partition first, and try to run it. If it failed to run, I would delete that copy and instead copy the disk into a 1541 emulation mode partition. If it failed to run from there, I'd give up in frustration and delete it from the hard drive.

CMD HD Directories

If a game consisted of more than a small number of well named files, I would create a subdirectory just for that game. And if possible, copy the file that I knew to be the boot file as the first file into that directory, and then copy in the other files.

Strange Loading Requirements

In some rare circumstances, where, for example, a game required Fastload to be disabled, or required being loaded with the £ DOS wedge command, sometimes I would save a file in that directory with a filename of "LOAD WITH £" or "DISABLE FASTLOAD" as a simple reminder to myself.

Device 8

This was truly a crapshoot. Even though some games could run from devices other than 8, it just became reflexive to hit the Swap button before attempting to load it.

Joystick in the wrong port

One shouldn't say the C64 follows no standards, even if they are only de facto. The vast majority of single player games uses the joystick on controller port 2. But, if I was unlucky enough to wait for a game to boot up, only to discover that it requires a joystick in port 1, I would usually take whatever risk there may be and hotswap the joystick.

An Intermediate Solution

Much more recently, following my return to the C64 in late 2016, I have been actively reorganizing my CMD HD. Almost all of the games I play are stored in partition 4, in various subdirectories. The default partition is 1, where I have my C64 OS development environment. For a while I would always issue the same set of commands to start up a given game, with JiffyDOS, something like this:

@cp4
@cd//itchio
↑doccosmos
Great game by the way, I love it! Get it here.

I know this game runs from the CMD HD, by trial and error, and my CMD HD is already device 8, although, sometimes I forget how it was spelled, "doc cosmos", "doccosmos" or "doc-cosmos" which necessitates listing the directory, or taking a shot at loading it like: "↑doc*". I must have loaded up Doc Cosmos 20 times—while working my way through its timeshifting caverns—before it dawned on me that I could put those 3 commands in a very tiny BASIC program, and save myself the effort each time I wanted to play.

Then I realized I could do this for all my installed games. I created a directory, in partition 1, called "menu", and it now contains a long flat list of "menu" entries for games I load most frequently.

Each game's menu item is only one file, regardless of how many files the game itself is made of. And because every menu item is a BASIC program, they can always be loaded and run with the JiffyDOS ↑ command. Each of these "menu" programs is like a small preloader. There are several nice advantages to using these:

  • The preloaders themselves are always loaded the same way
  • Every game is represented by precisely one preloader file
  • A preloader configures the machine—in a trivial but nonetheless convenient way—to prepare it to run the game

I only create a BASIC preloader, (conceptually a menu item,) when I am sure the game runs successfully from the CMD HD. Therefore assuring me, days, weeks or months down the road, that running any of the menu items is guaranteed to run that game successfully.

It sounds simple, but it makes the act of choosing: Which game to play tonight? less cognitively burdensome. Vortex Crystals? Doc Cosmos? Neutron? The Bear Essentials? Or a classic like, Fetris? Hostages? or Bruce Lee?


The CMD HD is not the only device on the bus though. On my main SD Card, in the uIEC/SD (on device 10), I have the complete backlog of Scene World in .D64 image files. The most recent issues definitely run from device 10, although I haven't tested the oldest issues. The naming convention used on the disk image filenames and the subdirectories they are in has changed slightly over time. And, from inside the first disk, it will ask you to swap disks. I have to remember the command to install the swaplist first, or generate a dynamic swaplist.

I don't know what it is, but this feels more burdensome than starting most games. I often make a mistake and have to try the commands more than once. I want to enjoy it on native hardware, but I also just want it to be easier. I want reliably launching an issue of Scene World to be as cognitively unburdensome as launching one of those games.

So I took the two most recent issues, created the disk swaplists manually, and created menu item BASIC programs that include the JiffyDOS commands to change the default device number (@#10) and to install the swaplist (@xs:swaplist), and then load and run the file that skips the introductory message.

And that works! It's great. Opening one of those two Scene World issues is now super easy. I'm still reading my way through issue 28.

Here's the thing though. I'm just creating these preloaders for myself, custom to my particular setup. Each BASIC program is standalone, so everything is repeated, if necessary, in each other one. And, I'm a JiffyDOS user, so these BASIC programs are full of JiffyDOS commands. If you don't have JiffyDOS or some other DOS wedge, these programs would have to contain more complex BASIC commands, with arguments and variables. Like this:

sd=10:ld=8
open15,sd,15,"u0>"+chr$(ld):close15
open15,ld,15,"cd//demos/scene world/swo-2":close15
open15,ld,15,"xs:swaplist":close15
load"boot file",ld,1

It's cool that you can do this, but we've gone from asking the user to type "load" and press return (for a stock tape cassette load), to having to write and save a small computer program, just to make loading the real program easier down the road!

I mean, it works. Having BASIC there is great. We've got a built-in scripting language at our fingertips, 2 seconds after we flip the power on. Hurray for that. But there is no way in hell this is user friendly, and it doesn't deal at all with additional complexities.

Capturing the Requirements

Here's what I think is needed. A standardized file format, that is incredibly lightweight, designed to be very easily stored, transferred, created and parsed by a C64, that captures the metadata relevant to loading, running and using the program. (In other words, its not important to me to have the publisher, year, programmer's name, etc. in this file format. We can use c64-wiki for that.)

There are some big differences between an Amiga and a C64, though. On an Amiga, you turn it on, and it boots up into a rich graphical user interface, with icons, windows, menus and mouse control. You can stick a disk in the drive and restart the computer, but now not only are you restarting the computer but you're also booting the game from floppy. How would you restart the computer but have the game automatically launch from a disk image in a drawer on the hard drive? Something about this on the Amiga just doesn't line up. It is much more convenient to simply double click an icon on the desktop. And on the Amiga, that's where WHDLoad comes in.

The C64 is, by its very nature, a much more primitive environment. You turn the machine on, and you're:

  1. Already in the raw configuration that every game expects the machine to be in. But
  2. You don't have an overarching friendly user environment whence to launch programs.

What you have is a BASIC interpreter that is 100% resident in ROM. You use BASIC commands to issue DOS commands directly to the drive, to tell it to:

  • Change current partition
  • Change current directory
  • Change its internal device number
  • Mount a disk image, or
  • Assign a swap list

Meanwhile the computer itself is still in the most raw configuration it can be in. The Amiga and the C64 are simply different in these respects.


But now let's think about the context of C64 OS. C64 OS is to the C64, something like Workbench is to the Amiga. The analogy isn't perfect. But it'll do some work for us.


Summing up an Amiga

Kickstart is a ROM that contains large portions of the Amiga's OS. The multi-tasking, some drawing primitives, some memory management, the drivers for accessing the floppy drives and hard drives, and more are all in the ROM. Workbench has to be loaded separately from storage into RAM, and builds upon what Kickstart provides, mostly in the form of a consistent and more sophisticated UI than the command line interface. And, there are different Kickstart versions which can lead to software compatibility issues.

Summing up a C64

The KERNAL and BASIC are ROMs, which contain large portions of the C64's OS. The main event loop, the fullscreen editor, some memory management, the drivers for RS-232, screen, keyboard, IEC bus, and more are all in ROM. C64 OS has to be loaded separately from storage into RAM, and builds upon what the KERNAL ROM provides, mostly in the form of a consistent and more sophisticated UI than the READY prompt. And, there are different KERNAL versions (like JiffyDOS) which can lead to software compatibility issues.


As I said, it isn't perfect, the Amiga is more sophisticated and much more powerful, but clearly the analogy can do some work for us. So let's run with it.

When you double click the icon of a WHDLoad installed game on the Amiga, here's what you are not doing: You are not just mounting a disk image and then generically trying to load and run the first file in the image. That's too primitive. Although, this is very nearly what we do on a C64 when we mount a .D64 on an SD2IEC and just try to load a game. It's too primitive. It can't handle the multitudinous ways that the hardware (and software) configuration might be "wrong" for the game.

I've created a C64 OS utility called PRG Runner. And to go along with this I've created a new type of desktop alias, the PRG Alias. Typically when you double click an alias on the desktop, App Launcher will open the C64 OS application or utility the alias points to. PRG Aliases are a bit different. They can be put on the desktop, assigned a color and moved around just like other aliases, but when double clicked they always open the PRG Runner utility. The App Launcher opens the utility and supplies it with a pointer to the name of the PRG Alias that was used to open it. The utility then opens a metadata file with a corresponding name, loads in its details and presents to the user a summary of that metadata. It gives some advice on what you may need to do and states what it needs to do. It will tell you if the program cannot be run. I'll go into all the details on this shortly.

The bottom of the utility has a RUN button. The user merely clicks the button to run the program. The PRG Runner utility performs a number of automated steps to reconfigure the C64 to be able to run this particular PRG. Here's how it looks:

PRG Runner Utility - Vegetables. PRG Runner Utility - Scene World. Scene World.
The rightmost screenshot shows Scene World running after being started by PRG Runner.

First let's dig into how it gets started and what sorts of things it can do.

How PRG Runner Gets Started

I'm not sure this is what I think the ultimate, final version of what I'm talking about should be like. But intead of getting stuck in an endless debate about the perfect format, I prefer to move forward in steps that are attainable now, that show marked improvement over what came before and then to use the latest ideas to springboard better ideas.

First let's see how App Launcher desktop aliases work. The system directory contains a desktop directory, and inside that are 5 numbered directories, 1, 2, 3, 4 and 5. The five numbered directories contain the aliases that appear on each of the App Launcher's 5 desktops. You can switch between desktops by menu option or keyboard command.

Each desktop alias is a SEQ (PETSCII text) file. The filename of the alias corresponds exactly with a couple of things:

  • The name of the alias as it appears on the desktop, and
  • The name of the thing for which it is an alias.

There is very little abstraction going on here. The content of an alias file is just 4 bytes:

  • X Position*
  • Y Position*
  • Color
  • Type
* Positions are measured in text rows/columns, not pixels.

The values in the alias file are in PETSCII, so that they can easily be edited with a text editor on a C64, (my preference is NovaText which is part of Novaterm 9.x). They'll ultimately be creatable from within App Launcher, of course, but for now they can also be created using the included BASIC program //os/desktop/:alias creator

A maximum of 32 aliases can be rendered on a single desktop. 32 times 5 desktops, means the App Launcher can manage a total of 160 aliases. These can be thought of as "things you access frequently." When you change desktops, all the aliases for that desktop are read in, and parsed into 32-byte memory structures. That's 8 alias structures per memory page, and thus 8 pages of memory are dynamically allocated when App Launcher first opens to accommodate all 32 aliases.

Paper notes on Desktop Aliases.
My paper notes are actually a bit out of date... I should update them!

When you double click an alias, or choose open from the pulldown menu, its type code determines what App Launcher will do with it. There are 4 types:

  • a = Application
  • s = Service Application
  • u = Utility
  • p = PRG (Not C64 OS native)

Applications and Service Applications are structured identically, but Service Applications are stored in the services directory in the system directory. They are separated from the standard applications directory, because the latter is a place for users to install apps, and if they don't want an app installed, it can be deleted or moved from the applications directory. Service Applications on the other hand are essential to the system and the File Manager will not allow you to delete them. However, both types are opened the same way, with a KERNAL call. The App Launcher is cleanly quit and the new application launched.

Utilities are opened alongside the current application, so Calculator, or Today, or Drives, etc. is opened in a floating palette above the App Launcher desktop.

For all of these the name of the alias corresponds with the name of the thing it opens. Thus a utility-type alias named "Calculator" opens a utility named "Calculator" found in the utilities directory. An application-type alias named "Chess" opens an application whose bundle name is "Chess" in the applications directory.

The PRG-type aliases are slightly different. Opening one of those opens the PRG Runner utility, which is also installed in utilities directory because it is a utility like any other. It is passed a pointer to the string name of the PRG alias. Let's say it's "Vegetables". The PRG Runner utility opens a metadata file named "Vegetables" and reads and parses in all of its data.

That's a lot of wordy description. Here are some sample paths and files:

//os/desktop/1/:Calculator       (Utility-type alias on desktop 1)
//os/utilities/:Calculator       (The calculator utility it opens)

//os/desktop/4/:Chess            (Application-type alias on desktop 4)
//os/applications/Chess/:main.o  (The Chess application bundle's main file)

//os/desktop/3/:Vegetables       (PRG-type alias on desktop 3)
//os/utilities/:PRG Runner       (The PRG Runner utility)
//os/prg aliases/:Vegetables     (The Vegetables PRG metadata file)

Ergo there is a separation between the App Launcher's desktop alias files, and the PRG Runner utility's PRG metadata files. I think this is a good separation of function. Even though it means you have to have two files, it doesn't make sense to smash together the PRG Runner's metadata files directly with the file format of App Launcher aliases.

App Launcher has an alias type for opening PRG Runner with a pointer to the PRG metadata file to read in, but there is nothing saying that only App Launcher can open the PRG Runner utility! Any C64 OS application, if it made sense to have the user leave C64 OS and run a regular C64 program, could programmatically open PRG Runner, pointing it at the metadata file for a C64 program. For example, some C64 OS app that organizes resources for creating games could embed a standard C64 program for editing fonts or sprites, and allow the user to launch directly into one of those. That's kind of a cool idea.

If you think about it this way around, it wouldn't make sense for the PRG Runner metadata file to be bound up with App Launcher's alias file format, as they really have nothing to do with each other.


PRG Metadata File

The PRG metadata file is similar in nature, though different in structure, to an App Launcher desktop alias file. It's a SEQ (PETSCII text) file, which can be edited with a standard text editor, and can easily be parsed or written out programmatically, and yet is very lightweight. A format like XML or JSON is totally out of the question, and even something simple like INI is a bit too much overhead, in my opinion.

Format of a PRG metadata file parsed into memory.
PRG metadata memory structure

The file is divided into three lines separated by a carriage return (0x13): Metadata, filename, directory path. Directory path comes last, because it's the only component with a flexible length.

metadata{0x13}
filename{0x13}
path

Here's an example metadata file, very simple text file:

cda11010000
zetawing v1.1
//itchio

The metadata line starts with three bytes:

  1. source device number
  2. launch partition number
  3. launch device number

PETSCII to INT Mapping: The value of these three bytes is PETSCII mapped. The PETSCII characters "a" to "w" correspond with device numbers from 8 to 30. Why do PETSCII mapping? Because PETSCII is easily typable, easily readable without any conversion if the file is output to screen, and yet each value is only 1 byte so parsing is super easy. It's always one byte, even for 2-digit device numbers "10", "11" etc. Simply subtracting the PETSCII value of "a" converts it to an integer. Or, to convert "a" to the integer 8, simply subtract 57. (PETSCII "a" = 65 and 65-57 = 8).

The launch partition number is also PETSCII mapped, but from "a" to "z" representing partition numbers from 1 to 26. Or "@" representing partition 0, which can be used on devices such as 1541, 1571, 1581 we don't have partitions. (Note that "@" in PETSCII is one less than "a", so the choice of "@" is not arbitrary.) SD2IEC devices support upto 16 partitions, and many support as few as only 2 partitions, so this covers all of them. On CMD devices that support up to 255 partitions, only the first 26 can be specified.

The launch device number is specified the same way as the source device number, with one small exception. The value can also be PETSCII "*", which means that it matches the source device number. More on this shortly.

Table of PETSCII Mappings
Character Source Device # Launch Partition # Launch Device #
@ 0
a 8 1 8
b 9 2 9
c 10 3 10
d 11 4 11
e 12 5 12
f 13 6 13
g 14 7 14
h 15 8 15
i 16 9 16
j 17 10 17
k 18 11 18
l 19 12 19
m 20 13 20
n 21 14 21
o 22 15 22
p 23 16 23
q 24 17 24
r 25 18 25
s 26 19 26
t 27 20 27
u 28 21 28
v 29 22 29
w 30 23 30
x 24
y 25
z 26
* source device #

PETSCII to BIT Mapping: Following these three bytes are a series of PETSCII "0" or "1" bytes, which are parsed into memory as individual bit values. Each byte is a flag with a defined meaning. There can be up to 16 of these, and if the line end is encountered before all 16, the remainders are set to 0. So far only 5 flags are defined, at least 2 more are planned, leaving 9 more reserved for future use.

Reserved meanings for the PRG metadata bit flags.
Reserved meanings for the PRG metadata bit flags

The order of the two flags bytes in memory are little endian, (low byte, high byte), but the order of the PETSCII bit flag bytes in the metadata file are in bit order lowest to highest. In other words in the metadata file, immediately following the 3 device and partition bytes is the first bit flag byte. The first one encountered is defined as the "BASIC loader" flag, which in memory is set as b0 of the low byte (first byte) of the two flags bytes. The second flag byte found in the metadata file is the "Shares IEC Bus" flag, which gets set in memory as b1 of the low byte (first byte), etc.

Order of PETSCII 1 and 0 bytes to bits in memory.
Order of PETSCII 1 and 0 bytes to bits in memory.

The second line contains the filename to load first. This is very straightforward. Because names are limited to 16 characters, the in memory structure reserves 17 bytes. Up to 16 characters plus a NULL terminator. If the filename is less than 16 characters, the memory structure gets padded with NULLs.

The third line contains the CMD-style DOS path to the file. This comes last because the path can be an arbitrary length. Therefore the path starts at offset 22 into the memory structure, but it can carry on all the way to the end of the page (up to 234 bytes.) We'll come back to talk more about filenames and path options below.

So let's talk about what this metadata can do, and what additional flags are planned.

What PRG Runner Can Do

Installation Locations

The Amiga has this very clever concept of "assigns". The OS, at a very low level, allows a device and path to be assigned an arbitrary label. That label can then be used as a device name in a path, and the OS resolves the actual location of the resource by using the assignment at runtime.

So the way WHDLoad makes use of this is kinda neat. One drawer is created for games, and another drawer is created for demos. Inside each of these drawers are 27 more drawers, one for each letter of the alphabet plus a numbered one (#). A game's disk images are collected in a drawer with the name of that game, which is filed in the correct alphabetic drawer of the games drawer. Same deal with demos. But then, the startup sequence includes an "assigns" command that assigns "a-games:" to whatever is the actual device and path where you've put the games drawer. And another assigns "a-demos:" to wherever the demos drawer actually is.

The WHDLoad slave interface for a given game then always finds the disk images for that game at a-games: followed by a path of the first letter of the name of the game, then the name of the game. And it matters not at all on what device a-games: actually is. Very clever. The only real limitation, (I believe, correct me if my knowledge of the Amiga is faulty) is that all of a-games: must be contained in a single physical volume. So, all your WHDLoad games disk images must be able to fit in that one volume. Then a-demos: can be assigned to a different device or partition, but all installed demos must be able to fit in that volume.


The C64, unfortunately, has no such abstraction layer. However, the C64 also has other limitations on volume size. For example, a CMD RamLink is only 16MB total, and a CMD HD can only have a maximum size of 16MB per native partition. If a single .D64 file (or its equivalent contents) is 177K, that's only ~92 disks-worth of content in a single volume. Given that there are far more than just 92 .D64s out there, we need a way to distribute them across partitions regardless.

With WHDLoad, the user can assign a-games: (and a-demos:) to where they want, but relative to those assigns the games have a very strongly predefined installation location. Setting aside whether this is desirable, I don't think this is possible on the C64. So we'll return later to talk about what parts of a metadata file are customizable and what parts are shareable, but just bear that in mind as we talk about the elements of the metadata file.


Let us suppose that we have a fairly sophisticated drive configuration.

Here's my own setup, which we can take as an example:

Device Capacity Partitions Media Device # Availablility
CMD HD 1GB 12 Fixed 8 Permanent*
1541 177KB 1 Swappable 9 Partial
1581 800KB 1 Swappable 10 Partial
uIEC/SD 1GB 2 Semi-Fixed* 12 Partial

Some notes: Obviously an SD Card is not truly fixed, but because of its enormous capacity it is primarily used in a fixed manner, only being removed temporarily to transfer contents from a PC/Mac. Also, a CMD HD can easily be turned off or disconnected, but in my particular setup it is my primary drive that I always use, and is also my C64 OS system drive. The floppy drives I often leave off, and the uIEC/SD (like all SD2IEC devices) can be put to sleep even when its still powered on and connected to the bus.


Device # Swapping

The metadata file has multiple device specific features, that are not related to the bit flags. Source device # is the device number on which this program is installed. So, if the program is installed on my uIEC, the source device # is 12 (in the metadata file that would be "e").

However, the program may or may not be able to run from that source device #. It may be installed on device 12, but it may require running on device 8. The launch device specifies the device number it must launch from. 99% of the time, if this is going to be different, it's going to be 8, as discussed at length in part 1 of this post. However, it can in practice be specified as any other required device #. Or, if the program is capable of running from any device # the launch device number can be specified as "*".

So, what happens if the source device # is different than the launch device #? The PRG Runner will tell you that it is going to swap the source device to some other device number. This will often show up as a "Swap# 8" beside the "Dev# 12" (or whatever is the source dev#.) Now, what will actually happen when you hit run? The C64 OS detected devices table is referenced to see if and what is already on the launch device #. If something is already on the launch device #, that device will have its number reassigned to the lowest available device #. Then the source device will be swapped into its place.

The C64 OS detected devices table is also used to know what devices are actually being swapped to know how to swap them. All devices except the 1541 support the "U0>" command, and the 1541 is supported with a memory write ("M-W") command.

What's beautiful is that checking if swapping is necessary is done on the fly, and then where to swap to and how to swap are handled automatically.

Partition #

Next we have the partition number. If the device supports multiple partitions the launch partition # can be in the metadata file. Changing current partitions happens automatically because the PRG metadata file gets parsed into a C64 OS file reference compatible struct. In other words, the PRG metadata struct can be passed directly to the the C64 OS finit KERNAL call, just as though it were a regular C64 OS file reference. If the device doesn't support partitions, the partition number will be ignored.

Partitions are transparent to the C64. All the devices that support partitions also support partition 0 as a relative reference to the drive's internally set current partition. 0 corresponds with drive mechanism 0 in non-partitioned devices. You can read more about this in my earlier post, KERNAL, File Refs and Services.

Directory Paths, Disk Images and Swaplists

Now let's talk directory paths. The directory path, of a device that supports them, is also transparent to the C64. If the path to where a game is installed is specified in the metadata file, that directory will be set as the current directory of the device. There is one additional feature that should be mentioned. SD2IEC devices have the ability to mount a .D64 image file (as well as the less common .D71 and .D81 and .DNP image files). This can be accomplished with the cd (change directory) command.

This follows a very standard command structure, but Commodore DOS can seem a bit arcane until you get used to it. When I first played around with mounting images, I thought it was only possible to mount a .D64 from the current directory. For example, the following command does not work:

cd//games/itchio/neutron.d64

This produces an error. However, the following two commands in succession do work, which led me to conclude that PRG Runner will need to issue two commands.

cd//games/itchio
cd:neutron.d64

However, I discovered something else (and subsequently updated the documentation in the SD2IEC User's Manual, which I hope to be an invaluable resource for all SD2IEC users.) It is indeed possible to mount a .D64 with a path in a single command. The command and path must be structured like this:

cd//games/itchio/:neutron.d64

The full colon separating the path from the .D64 filename allows a single cd command to be used. Therefore, mounting a single .D64 image is possible by including it in the path with the colon.

Before we move on from the path and mounting a .D64 image, we need to talk about swaplists. SD2IEC supports assigning a swaplist (one per partition). The assigned swaplist contains a list of disk image files to mount.3 Pushing the next and previous buttons on the SD2IEC device will tell the hardware to mount the next or previous image in the list. So we need a way for PRG Runner to know about and handle swaplists. One of the 16 metadata bit flags is used for this purpose. It's the Path to Swaplist flag. If this flag is set, it indicates that the path component of the metadata file includes the name of a swaplist. Rather than using the cd command, it uses the xs command instead. Like this:

xs//games/origin/space rogue/:swaplist

Note that just as a .D64 file can be specified at the end of the path by using the colon, a swaplist file can be specified at the end of the path component of the xs command. And not only that, but the files listed inside the swaplist are relative to the path where the swaplist lives. So, assigning the swaplist above, it can contain just "disk1.d64" and "disk2.d64" and they will be found in //games/origin/space rogue/ (of the current partition.) Whenever a swaplist is assigned, the first listed image file is automatically mounted too. That's pretty great.

Boot Filename

To load the program the metadata file contains a single filename. Maximum length of the filename is 16 characters. Now, if the metadata file specifies a .D64 image or a swaplist for images, the filename could likely be safely specified as "*". This will work as the equivalent of a load "*" command you'd typically issue from the READY prompt, matching the first file in the disk image.

If no disk image is being used, a filename of "*" may still work, of course, but it is less safe and less reliable for the metadata file to specify it that way. For example, if the partition and path lead to a set of files for just a single game, and the boot file is the first file in that directory, or if the install device is actually a genuine 1541, 1571 or 1581 with a commercial disk in it, "*" will work. However, it is much more reliable in this case for the metadata file to actually specify the name of the boot file. For one, it's free! It's just a few extra bytes in the metadata file, that the user doesn't have to type anyway. So why not? And, if the files from multiple programs are dumped into a single directory, or if the order of the files in the directory get changed (such as by a file copier that doesn't copy the files in their original order,) the name will properly match the boot file of that program.

In my personal opinion, frankly even if it does specify a .D64 or swaplist, "*" should be avoided as the boot filename.

Swappable Media

There is no specific metadata that indicates the device uses swappable media. However, if the source device is a 1541, 1571, 1581 or CMD FD, which is deteremined at runtime by referencing the C64 OS detected devices table, PRG Runner will warn you with a message:

INSERT DISK!

This is handy. I created an alias on the App Launcher desktop to the game, The Bear Essentials, which I purchased on 1541 floppy. The metadata indicates that the source device is 9 and the launch device is 8. The C64 OS device detection table indicates that 8 is currently occupied by my CMD HD, and that 9 is a physical 1541. It presents the message INSERT DISK!, as a reminder to put The Bear Essentials disk in the drive. It also states that it will swap to device 8. When you hit run, the CMD HD gets reassigned to device 11 (the lowest one not used in my configuration as mentioned above), and the 1541 is reassigned to device 8. And then the boot filename is loaded explicitly and run from the disk.

There is currently no confirmation that the disk in the drive is indeed the correct disk. Would that be useful to have? Would it be useful to have a spot in the metadata file for the two byte disk ID? It wouldn't be relevant for anything other than the original floppies, as the correct .D64s get mounted explicitly by their name. We might want to discuss this.

Device Impermenance

As noted above, some drives are always available while others are not. My CMD HD is my primary storage device. My 1084s monitor and CMD HD are left on and plugged into a power bar with a master on/off switch. This saves wear on their switches and makes it easier to get going, just one power button to activate multiple devices. It also points out, I never turn off my CMD HD.

The CMD HD is also used as my C64 OS system drive, so it cannot be unmounted/ejected from the C64 OS Drives utility. If this sounds foreign on a C64, just think about it on your PC or Mac. You can't just turn off the C: drive that Windows is booted from! Nor can you drag the startup volume (Macintosh HD) to the trash can to eject it. Well, you can't just turn off your C64 OS system drive either, and expect things to keep working.

It is generally common on a C64, though, for drives to join and leave the bus. Every drive has its own power supply,4 and its own power switch. I usually leave my 1541 turned off when I'm not planning on using it. This is recommended practice because the 1541 tends to run hot and should only be left on when its going to be used. PRG Runner uses the C64 OS detected devices table to notice whether the source device is missing. If so, it gives you a warning...

DEVICE NOT PRESENT

And the RUN button is disabled. You can use the aforementioned Drives utility to list the currently detected drives. Turn on a new drive and press the rescan button to rerun the drive detection routine and update the detected drives table. Easy peasy. You should also use the Drives utility if you turn a drive off, precisely because you want the system to have an accurate representation of which drives are currently available, because programs like PRG Runner rely on the system for that information.

Other Metadata Flags

Let's talk about the metadata flags.

  1. BASIC Loader
  2. Shares IEC Bus
  3. Joyport 1
  4. Joyport 2
  5. Path to Swaplist

These are the ones I'm supporting so far. The next obvious addition is a flag or flags for NTSC/PAL compatibility.

BASIC Loader

As discussed at length in part 1, a major source of inconsistency for loading a C64 program is whether it contains a BASIC pre-run program or not. Whether it is a full BASIC program, or an assembly program but with a BASIC starter, it is going to load to $0801, because that's where all C64 BASIC programs must start. So, if this flag is set, PRG Runner will load it to $0801, regardless of its 2-byte header. (This is the equivalent of using the LOAD command without the final ,1)

Additionally, when the BASIC loader flag is set, BASIC is instructed to run the loaded program. If this flag is not set, the program will be loaded to its 2-byte load header. And this address is automatically jumped to via a simple JMP instruction to start the program. I've tested this, and PRG Runner can run Hat Trick (as mentioned in part 1), and it can also run a machine language monitor loaded to $c000, etc. It is not necessary to put the load address in the metadata file, as this is read from the header of the PRG file itself.

Shares IEC Bus

This issue is very idiosyncratic to the C64. The C64, especially with its stock KERNAL ROM, is notoriously slow at IEC bus communications. To load a 202 block PRG file from a 1541 with the stock KERNAL takes 124 seconds (>2 minutes). According to the JiffyDOS user manual. There have been numerous solutions for speeding things up. With JiffyDOS, for example, you can load the same 202 block PRG from a JiffyDOS equipped 1541 in just 12 seconds. That's a 10X increase in load time, not bad considering its only a software change.

Jim Brain explained to me once at World of Commodore that JiffyDOS is not the fastest solution ever devised, but it strikes a careful balance of speed and compatibility. Not only is it dramatically faster than the stock KERNAL, but there are very few downsides.5 JiffyDOS works with all known drive types, and can improve access times for 1541, 1571 and 1581, if their internal ROM is updated to a JiffyDOS ROM. The CMD FD, CMD HD and SD2IEC drives have support for the JiffyDOS protocol built-in. JiffyDOS also supports, as the stock protocol does, multiple drives on the bus at the same time.

Not every software-based speedloader is so ecumenical. Many software-based speedloaders only work with a 1541, and crazier yet, many obtain their extraordinary performance by completely redefining how the lines in the IEC cable are used. By using the handshaking line to also transport data, higher transfer rates can be obtained but at the cost of not being able to address more than one device on the bus. In fact, these routines usually fail if another drive is on the bus. The constant manipulation of the handshaking line confuses the other drives and leads them to affect the data line at unexpected times, corrupting communications with the intended drive.

The most conspicuous game that uses this technique in modern times is Sam's Journey. By all accounts a fantastic game, and a wonderful achievement on the C64. But it cannot be booted at all, you just get a blank black screen, if the boot device is not alone on the bus. You don't even get an error message. You just have to know about this by reading the user manual. This is a prime candidate for a PRG Runner metadata flag. The Shares IEC bus flag is usually set, indicating that a game will work with multiple drives on the bus. But if it is unset, PRG Runner will present an error message and grey out the RUN button if the source drive is not the only drive on the bus. Again, it determines whether other drives are on the bus by reference to the C64 OS detected drives table.

JoyPorts 1 and 2

These bits indicate which controller ports are used by the game. In most typical games the master joystick is in port 2. This is convenient, because in most programs that use the 1351 or compatible mouse expect it to be in port 1. C64 OS requires the 1351 in port 1, and so on my system I have a joystick permenantly in port 2 and a mouse permenantly in port 1.

If the game requires that the master joystick be in port 1, well, you have a bit of a problem. But at least you know what to expect.

If you, like me, have just a joystick and a mouse hooked up directly to both ports, there isn't much you can do. Because I would not recommend unplugging the 1351 mouse with the power on as the device has sensitive electronic components inside, and active communications are happening all the time. If you merely forgot to plug a joystick into port 2, for most basic joysticks you're probably pretty safe to just plug it in hot, although I'm not recommending that either.

This feature is very handy when paired with some other new hardware though. There are at least two pieces of hardware which make this useful.

Joystick port switcher. Joystick port switcher with built-in PS/2 mouse adapter.

There are actually a variety of these joystick port swappers, I have a few listed in the Commodore 8-bit Buyer's Guide. The one on the right, above, has something even better: A PS/2 to 1351 mouse adaptor! Ideal for use with C64 OS. You get a 1351 compatible mouse via a much more commonly available PS/2 mouse, plus you get to have two joysticks plugged in at the same time as the mouse, and you can act upon information from PRG Runner by simply pressing the joystick swap button.

NTSC/PAL

I haven't implement support for this in flags yet. But, I think for maximum compatibility you need to use at least 2 bits. While it makes no sense that a game would support neither NTSC nor PAL, there are some games that support both, or one, or the other.

Creating, uploading, downloading, customizing PRG aliases

This post is getting long, and I'm way behind schedule releasing it, due to a number of factors, including an incident of COVID-19 at my daughters school causing her to be home in self-isolation for almost 2 weeks. So I won't spend too much time on this part of the discussion.

Essentially, you have to choose where to install a game. If the game comes in a .D64 (legally speaking, perhaps you make a .D64 backup copy of a game you own on disk), you have to put the .D64 into a directory, on a device, potentially in a partition other than 1. This is going to be different for every user.

What will not be different though is the main metadata:

  • Launch device number
  • Boot filename
  • BASIC Loader
  • Shares IEC Bus
  • Joy Port 1
  • Joy Port 2
  • NTSC
  • PAL

A game either comes as one or more free floating files, or one or more disk images. If there is more than one disk image, the collection of images should include a swaplist file that contains the disk image filenames in order.

We can't distribute the copyrighted games, but we can provide the metadata files for those games just like WHDLoad's slave interfaces. The way I see this working is that besides the PRG Runner utility, there ought to be a PRG Installer application. You procure the game for yourself, be that on disk, .D64/71/81 or just plain files. Put the .D64s or files in a directory, where ever you want to keep them. Use the PRG Installer application to download the game's metadata file. Click a button to specify the location, which opens the Files utility. Navigate to the directory containing either the .D64 or the files (including the root directory of a disk). The Files utility passes a file reference back to PRG Installer application, which combines the path and source device information with the other metadata and saves it to the standard PRG aliases directory. (//os/prg aliases/).

Additionally the PRG Installer could offer to add an alias to the game to one of your App Launcher desktops.

I'm not sure how to handle swaplists. But perhaps PRG Installer could create the swaplist for you if you selected more than one .D64 image.

One more complication is that many games are available from online sources repackaged in different ways, having been cracked by different groups. Each repackaging changes the name of the file to be booted, and can even change certain requirements on IEC Bus sharing, or BASIC Loader, and some versions may even be NTSC-fixed. Ideally a metadata file would be tied directly to a specific .D64 version of a game. Unfortunately, I can't think of a legal way to do this. The PRG metadata files therefore should probably be configured for the original published release of the game. And the multitude of cracked versions, well, you can always make your own PRG aliases.

Now, speaking of creating your own PRG aliases. In my opinion, another purpose of the PRG Installer app should be to create the PRG metadata files for commercial games that don't yet have one. The installed location of the game is not relevant, because it will be different for each person who installs it. However, upon testing a game out, and determining the name of the boot file, that it can share the IEC bus, that it's primary controller is in port 2, that it is NTSC and PAL compatible, it should be a simple matter of choosing to create a new PRG metadata file, checking the appropriate boxes, selecting the boot filename, and typing in the name of the game, and hitting save.

But, of course, what's really cool perhaps hitting "Upload", and having the new metadata file sent to a common only repository whence PRG Installer on other C64s can find it to download.

Final Thoughts

I'm way far behind on publishing this post. And this, part 2, is now over 11,000 words. So I'm going to leave it here and write a third post to talk about the actual technical details of how PRG Runner works.

  1. Included as a physical 1541 is a modern strongly compatible clone, such as Pi1541 or 1541 Ultimate.
  2. This could be in the form of either a 1541 emulation partition on a CMD storage device or a .D64 image that is mounted by an SD2IEC device. This requirement is less stringent than a physical 1541.
  3. Actually, it's not just disk images. A swaplist technically contains a list of arguments that the cd command can take. So a swaplist can change mounted disk images, but it can also just change the current directory to some path.
  4. There are actually some exceptions to this nowadays. Many SD2IEC devices are sufficiently low-power that they are able to be powered from the C64 itself, often via a cassette port adapter. SD2IEC devices generally also don't have power switches. But they do have a sleep mode, which can be invoked at any time to effectively turn the drive off.
  5. There aren't zero downsides. The datasette (cassette drive) is unsupported when JiffyDOS is enabled. They reasoned that most people who were interested in JiffyDOS to get huge speed improvements on their floppy drives and hard drives, probably aren't that interested in using the much older, slower and less technically capable tape drive.

Do you like what you see?

You've just read one of my high-quality, long-form, weblog posts, for free! First, thank you for your interest, it makes producing this content feel worthwhile. I love to hear your input and feedback in the forums below. And I do my best to answer every question.

I'm creating C64 OS and documenting my progress along the way, to give something to you and contribute to the Commodore community. Please consider purchasing one of the items I am currently offering or making a small donation, to help me continue to bring you updates, in-depth technical discussions and programming reference. Your generous support is greatly appreciated.

Greg Naçu — C64OS.com

Want to support my hard work? Here's how!