Omega Race on the C64 — A Tale of Two Cartridges

A quite literal look into a curious duplication.

title illustration

Ppreviously, we briefly mentioned that there was a Ultimax cartridge of the game “Omega Race”, that this was playable on the Commodore C64, and how this demonstrated the rather deliberate embedding of font data in this type of cartridge.
So that’s certainly good to know and we’re quite glad to have established that. … Well… Hmm… But wait, this is by no means all: there are actually two “Omega Race” cartridges for the C64!
Once said Ultimax cartridge and then there is a dedicated C64 cartridge, as well.

Since the C64 plays Ultimax cartridges quite perfectly out-of-the-box and both cartridges are similar in ROM size, 8K, this may be somewhat puzzling. Why even bother doing it this way and not just go with a single, universal format?

Well, what is there to do, but having a closer look at this. This time, quite literally so.

But before we do this, we have, you guessed it, to have still a look at…

The Nitty-Gritty Details of C64 Cartridge Operations

In order to explain cartridges for the C64, we have to start at the very beginning. As in turning on the machine. What happens then is that a simple timing circuit puts the CPU a few moment in a reset state, by pulling the signal on a special pin low (to GND), until power has distributed through the system and the various components are in a stable state. Next thing, as the reset line on the CPU goes high (+5V), indicating normal operations, as the CPU is said “to come out of reset”, the CPU inspects a certain hard-coded address, the RESET vector at $FFFC and $FFFD (low byte, high byte), and starts to execute whatever code there is at the address given in these two memory locations. This way, we have a well-defined, but still configurable, start-up sequence, which we can rely on.

MCS 6510 Pinout
Admire the RESet signal (active low) at pin 40 of the MOS 6510 CPU, at the very top, right side.

So, what’s at these addresses? The C64 ROM, easy! Well, it may be, but it may be also not. It depends. More precisely, it depends on the signals found at the cartridge port.

C64 cartridge port
The cartridge / expansion port of the Commodore C64.

The cartridge port of the C64 features two signals, which should be interesting to us in this context: “_GAME” and “_XROM”. If _XROM is low (0: GND) and _GAME is high (1: +5V), the C64 configures in Ultimax mode.

Ultimax

So who or what is a Ultimax? Well, the Commodore Max, AKA Commodore Max Machine (in Japan), or Ultimax (in the USA), or VC-10 (in Germany, announced but never released), was a short-lived attempt by Commodore on a low-butget machine geared towards playing video games. It was developed at the same time as the C64 and shared its core components: the 6510 CPU, the VIC II for graphics, the SID for sound, but, since it cost-effectively lacked the essential communication ports like the user port or the serial port, it came with just a single CIA. Also, it had just 4K of RAM and no ROM, at all, relying entirely on cartridges for its operations. Namely, for out-zonking, out-zapping, out-singing and — last but not least — out-selling the competition. Not much became of the latter as the [Ulti]Max [Machine]™ was discontinued the same year that saw its introduction.

Pamphlet: Introducing the Commodore Max Machine (Commodore, 1982)
Commodore pamphlet “Introducing the Commodore Max Machine™.”, 1982 (excerpt).

(As a maybe interesting aside, in Germany the Max was announced together with the C64, the Max featuring as the VC-10 and the C64 as the VC-40, bracketing at both sides of the product lineup the venerable VC-20, which in its German incarnation lost the “I” in its name for much the same reasons that Puck-Man became Pac-Man in the USA. This parallel marketing effort may illustrate how the C64 and the [Ulti]Max [Machine]™ were really meant to go in tandem, much like the Atari 400 and the Atari 800. Even the US pamphlets for the two machines share a similar style and mood, emphasizing the parallel effort.)

With RAM, ROM, and fullt-travel keyboards contributing significantly to overall costs of home com­puter production, the [Ulti]Max [Machine]™ did its best to reduce its budget effictively.

Thus, the Ultimax features the following memory map:

$0000-$0FFF  ....  RAM   (4K)  internal
$1000-$3FFF  ....   -
$4000-$7FFF  ....   -
$8000-$9FFF  ....  ROML  (8K)  external
$A000-$BFFF  ....   -
$C000-$CFFF  ....   -
$D000-$DFFF  ....  I/O
$E000-$FFFF  ....  ROMH  (8K)  external

which is exactly how the C64 configures when a low _XROM signal and a high (normal) _GAME signal is detected at the cartridge port. This is entirely a hardware thing. As the CPU comes out of reset, what it will see as the RESET vector at $FFFC is the cartridge ROM. At this point, it’s already a fait accompli, the C64 is a Max Machine.

Mind that there are two signals at the cartridge port, which allow the machine to signal which one of the two possible ROM banks it is addressing (_ROML and _ROMH, see above). But, since the CPU has to find a RESET vector in order to start successfully, this necessarily defaults to the 8K in the ROMH bank at $E000 to $FFFF. Which is what all the Ultimax cartridges do.

The important part to be kept in mind is really that, in this mode, the C64 has none of its built-in ROM available and lost most of its RAM (with just 4K remaining), and, just like the Ultimax, has to rely on the cartridge ROM alone, because it doesn’t just behave like the Ultimax, it really is another Ultimax.

(Previously, we saw that this is how the “Dead Test” diagnostic cartridge for the C64 manages without any of the built-in ROMs (they are really invisible in Ultimax mode). But this comes also at the price of “seeing” just the lowest 4K out of the total 64K of memory. The two signals for selecting the in-cartridge ROM banks, otherwise mostly unused, also explain how there could have been an extended revision of this cartridge with code at $8000.)

C64 Game Cartridges

When _GAME is low (0: GND) and _XROM is high (1: +5V), the story is an entirely different one. In this case, we’re dealing with a C64 game cartridge. The machine will be configured as normal, to standard C64 memory layout, the single difference being that the cartridge ROM will be exposed in the 8K address range from $8000 to $9FFF. All, the hardware does, is inserting the cartridge ROM in this memory range instead of the built-in RAM that is normally found at these addresses.

This also means that the start-up process for a cartridge must rely in this instance on software.
What the CPU “sees” is the usual RESET vector in the Kernal ROM, and we’ll have to follow this:

C64 Reset Sequence

addr  code         disassembly      comments

FFFC: E2 FC                        ;RESET vector: $FCE2
 

FCE2  A2 FF        LDX #$FF        ;value for satck pointer
FCE4  78           SEI             ;disable interrupts
FCE5  9A           TXS             ;initialize stack pointer
FCE6  D8           CLD             ;clear BCD mode
FCE7  20 02 FD     JSR $FD02       ;check for autostart ROM at $8000
FCEA  D0 03        BNE $FCEF       ;not found? skip...
FCEC  6C 00 80     JMP ($8000)     ;cartridge cold start vector
FCEF  ...

 
FD02  A2 05        LDX #$05        ;check for string "CBM80" at $8004
FD04  BD 0F FD     LDA $FD0F,X     ;compare 5 bytes at $FD10 to $8004
FD07  DD 03 80     CMP $8003,X
FD0A  D0 03        BNE $FD0F       ;no match, skip to exit
FD0C  CA           DEX
FD0D  D0 F5        BNE $FD04       ;loop (X: 5..1)
FD0F  60           RTS             ;return state in zero flag (0: found)
 

FD10: C3 C2 CD 38 30               ;CBM80

The RESET vector at $FFFC points to $FCE2, where the 6510 processor will start executing code. This first loads the value for initializing the processor’s stack pointer into the X register, to point to the top of the 256-byte stack. Then, we make sure that we’re not disturbed by any interrupt request (IRQ) by setting the “i” (interrupt disable) flag.
(A true connoisseur may ask why we didn’t begin with this. Well, the ROM of the PET, which this code was derived from, doesn’t feature any SEI instruction, at all, nowhere in the start sequence. So, I guess, this is better than nothing. Still…)

Anyway, we continue by setting the stack pointer (SP) and clear the decimal flag for normal, binary arithmetics. By this, we’re set up for essential operations and ready to go. The first thing, the code does, is to divert to a subroutine at $FD02. Here, the 5 bytes starting at address $8004 are compared to the ‘magic’ string “CBM80” in upper-case PETSCII. This routine returns with the result in the zero flag. If it is unset (clear), we found the string and jump to the address found in $8000 and $8001 (low-byte, high-byte) by an indirect jump.

Conversely, this is what the very start of the ROM in a C64 game cartridge looks like (here “Omega Race”):

8000: 09 80                     ;cold start vector: $8009
8002: D8 80                     ;warm start vector: $80D8
8004: C3 C2 CD 38 30            ;CBM80
8009: ...

The important part here is that this is still a normal C64, it‘s just that the ROM is made available instantly in the address range. However, in case it’s an autostart cartridge with the magic string “CBM80” at $8004, the cartridge has to bootstrap on its own, has to stand on its own feet: while the built-in ROMs and their various routines will be still available, as is the character ROM, the remainder of the start sequence and the initialization of the machine is left to the cartridge code.

So, you may ask, what if both _GAME and _XROM are low? Well, then it’s a 16K C64 cartridge and 16K of cartridge ROM will be exposed at $8000$BFFF (ROML at $8000$9FFF, ROMH at $A000$BFFF). Since only the very beginning of any ROM (at $8000) matters for the start sequence, this works just the same with 16K cartridges.

Omega Race, ×2

Now that we know this, how the games come onto the C64 and what this implies, we may have actually some fun with them!

So, let’s have a look, quite literally. For a difference, we won’t inspect hex-dumps or disassemblies of the code, we just look at it, in a visualization of the last (top) 2K of the ROM, where most of the game data resides. Let’s begin with the Ultimax version, visualizing the ROM from $F800 to $FFFF, lined up in neat little blocks of 8 bytes, each, left to right, top to bottom.

Visualization of the top 2K of the Omega Race Ultimax game cartridge.
“Omega Race”, Ultimax cartridge (start address 0xE000), 0xF800–$FFFF.
Blue: font data (character matrices), grey: data/code.

We may immediately discern the character data for the font, illustrating that in Ultimax mode there is no true or conventional order to characters, as this is neither in PETSCII nor in Commodore screen code order. Rather, relying on its own drawing routines, the cartridge does its own thing. The font is mostly identical to the standard C64 font, but features special stylized versions of the figure “9” and the letter “P” (each missing a pixel — this isn‘t a fluke in the ROM image, I actually do remember the peculiar “9” from back in the day).

Omega Race Ultimax font versus C64 font
“Omega Race” Ultimax font (blue) versus C64 font (grey).

This is followed by special characters to indicate function keys, followed by graphics for the borders and central box, a ship indicator and the “android” ships. This is in turn followed by various tables of game data, and (I believe) some actual code at the end. (The last 6 bytes are the CPU vectors, all 3 pointing to $E000, with the RESET vector second to last.)

And here‘s the dedicated C64 version of the game:

Visualization of the top 2K of the Omega Race C64 game cartridge.
“Omega Race”, C64 cartridge (start address 0x8000), 0x9800–$9FFF.
Blue: font data (character matrices), grey: data/code.

Since the dedicated C64 version of the game can make use of the internal character ROM, there is no need for alphabetic font data. Instead, there’s some (additional) code and/or data. But then we see some similarities to the Ultimax version: the characters (, ), and ;. Adjacent to this, there are some very special characters, apparently for a stylized title. Then, the graphics data is mostly the same, with another title character in place of the upright ship icon (which seems to go unused in the Ultimax game.) Even the game data is apparently much the same. There are some additional data blocks, though, both in “holes” (or blank spaces) in the data for the ship graphics and filling some spaces in the data section, which were empty in the Ultimax version.

We can overlap the tow images to see the differences:

Differences in the C64 and Ultimax versions of Omega Race.
“Omega Race”, differences in the game data.
blue: set in the C64 version, orange: set in the Ultimax version, green: set in both.

We may immediately grasp that these are essentially the same game. There are a few differences in the data/code section at the very end, which can be attributed to differences in addresses and referrring to different routines. But there are also these additional data blocks in the C64 version, for the most spread over the “holes” in the graphics data.

Some if not most of this is related to differences in sound: the C64 version attempts to show off the capapilities of the SID by a resonant, but also quite repetitive android sound, which is, frankly speaking, rather annoying. The Ultimax version does with a simpler, unfiltered sound, which may be also a bit more pleasant. Personal preferences aside, the C64 version requires additional tables for filter data and so on, in order to achieve its more complex soundscape.

And, indeed, gameplay-wise, the two cartridges do play very much the same. The only difference being that the Ultimax version surprises with a color cycling effect during any explosions of the player’s ship, where the C64 does not. It’s rather impressive that the “bare metal” Ultimax version, which can’t rely on any built-in ROM routines and requires additional embedded font data manages to do the same as the dedicated C64 version. Conversely, it may be a bit disappointing, that the C64 version doesn’t achieve more, even with the additonal ROM space spent, where the Ultimax cartridge has its embedded alphanumeric font data.

BTW, regarding embedded font data, it’s kind of interesting that there should be those redundant characters (, ), and ; embedded in the C64 version. These are already in the character ROM! Conversely, we find the redundant repetition of the figures 0 and 1 in the Ultimax version of the game, just adjacent to this. Raising the question, which one actually came first?

(BTW, said “holes” in the character data may probably be attributed to the internal data layout so that animation phases and display types may be addressed by index, just using simple shifts and ORs.)

Speaking of the title screen, this is where we actually find visual differences in the two versions of the game:

Omega Race, title screen of the dedicated C64 cartridge.
“Omega Race”, title screen of the dedicated C64 cartridge.
(Screenshot of emulation in VICE.)

The title screen of the C64 version is somewhat bland and wasn’t received well. There’s just a plain list of options for the input mode and background and foreground colors, and, yes, at least some color in the stylized title. However, as far as stylized titles go — and in order to understand why this may have been disappointing —, just compare this to the VIC-20 cartridge, also © 1982:

Omega Race, title screen of the VIC-20 cartridge.
“Omega Race”, title screen of the VIC-20 cartridge.
(Source: Moby Games.)

That’s a stylized title! Given the much advertised capabilities of the VIC’s “big brother” this was more of a down-grade and certainly not inspiring.

And here’s the title screen of the Ultimax cartridge:

Omega Race, title screen of the Ultimax cartridge.
“Omega Race”, title screen of the Ultimax cartridge.
(Screenshot of emulation in VICE.)

There are a few things of note:

For a convenient comparison, here are the two start/title screens side-by-side:

Omega Race, title screen of the dedicated C64 cartridge. Omega Race, title screen of the Ultimax cartridge.

The Ultimax screen certainly makes for a more sophisticated appearance by the structure added by the stylized F-key indicators. The C64 version, while otherwise rather bland, makes at least some attempt on an actual title for the game.

However, to understand what’s really going on here, we must have a look at…

Omega Race for the VIC-20

The VIC-20 version of the game, also released on cartridge (VIC-1924) and also bearing a copyright date of 1982, was a remarkable and much acclaimed arcade port, still figuring as one of the top-10 games on the platform to this day.

Now have a look at the title screen of the VIC version. It isn’t just a title screen, it’s an entire title sequence consisting of 5 individual screen cycling in a loop:

#1 – The actual title (as we‘ve seen, this isn‘t to be taken for granted):

Omega Race, title screen of the VIC-20 cartridge.
“Omega Race”, VIC-20, title.
(Source: Moby Games.)

#2 – Game options, the function key markers in reverse video blink and cycle in color (this is the equivalent to the start screen of the C64 and Ultimax versions):

Omega Race for the VIC-20, Game options.
“Omega Race”, VIC-20, game options.
(Source: Moby Games.)

#3 – We actually get a story (this is taken directly from the 1981 arcade game):

Omega Race for the VIC-20, Story (part 1).
“Omega Race”, VIC-20, story (pt. 1).
(Source: Moby Games.)

#4 – For the true arcade feeling, we even get a scoring table:

Omega Race for the VIC-20, Scoring table.
“Omega Race”, VIC-20, scoring table.
(Source: Moby Games.)

#5 – Finally, the story continues:

Omega Race for the VIC-20, Story (part 2).
“Omega Race”, VIC-20, story (pt. 2).
(Source: Moby Games.)

This is even more remarkable, since the VIC version manages to squeeze all this into just the same amount of 8K ROM that’s available to the C64 and Ultimax versions!

There‘s also a feature found in the VIC version that‘s missing in the C64 and Ultimax versions: when holding the SHIFT key while pressing the respective function key to start a game either with joystick or paddle controls, you get 5 instead of the usual 3 ships/lives. On the other hand, the VIC version is lacking an option for keyboard controls.

With regard to this, both the dedicated C64 version and the Ultimax version are restrained to just the options screen, dropping the note on the bonus ships for the indispensable copyright message(s). While the Ultimax version preserves the stylized F-key markers (less the blink and color-cycle effect), the C64 rather decides to have an attempt at the title. (You can’t have both, this would be preposterous! Just pick one!) However, rather than going for the actual logo (maybe using sprites?), similar to the VIC version, we get just a blocky line of text. — Well, it‘s a much more powerful machine and we can’t just recklessly let loose these powers unrestrained and out of control…

As for our initial question, it’s still hard to tell from this which version may be closer to an original idea and/or concept. However, it really doesn‘t make much sense to come up with a dedicated C64 version when you‘re just releasing a system with some notable effort and investment involved to run the same cartridges on both the C64 and the [Ulti]Max [Machine]™. Which may suggest that the dedicated C64 version should have come first. (Which would make the changes in the start screen even more remarkable, since the the Ultimax version could probably have just gone the same with some kind of title display instead of the stylized F-key markers, in the same memory constraints.) But, just the same, it could be that both versions were developed at once with a shared code base. Which would render some of the decisions even more puzzling.

Anyhow, just in case you wouldn‘t know, here is what the actual game looks like:

Omega Race, gameplay on the arcade machine.
“Omega Race”, gameplay on 1981 vector arcade machine.
(Source: Moby Games.)
Omega Race, gameplay on the VIC-20.
“Omega Race”, gameplay on the VIC-20.
(Source: Moby Games.)
Omega Race, gameplay on the C64 (C64 and Ultimax).
“Omega Race”, gameplay on the C64 (C64 and Ultimax cartridges).
(Screenshot of emulation in VICE.)

And by this, there‘s nothing left, but to close on the Brecht quote,

We’re disappointed too and see with a frown
All questions open as the curtain comes down.

*) This may be somewhat more effective in the German orignal, playing on opposites shut and open, while rhyming our disconcertment (it’s not just a frown) with the final openess of the still unresolved questions:

Wir stehen selbst enttäuscht und sehn betroffen
Den Vorhang zu und alle Fragen offen.

Well, translations, again…