NEWS, EDITORIALS, REFERENCE
Anatomy of a Koala Viewer
After 10 years away from the Commodore scene, the most shocking part of coming back was not the speed of a 1 Mhz clock, or even a 320x200 16-color display. The most shocking part is the essential interaction model. And this was my inspiration to start working on C64 OS. GEOS, as I've mentioned in many of my posts, comes much closer to what you would expect from a modern computer. But even it has all kinds of oddities that show its age. And unfortunately, its fully bitmapped UI is just too damn slow to get used to. Using a C64's READY prompt to do ordinary computing tasks is truly a blast from my adolescent past. But returning after a few years away and learning all over again how it works, it feels like a totally foreign world.
Nothing better exemplifies this than my most recent experience working with koala image files and a koala viewer program. So I'm going to deep dive on exactly what I mean by totally foreign and discuss how C64 OS will work to make the whole model more modern.
What is Koala?
Koala was a suite of drawing technologies for a number of computer platforms in the 80s. You can read all about it on this Wikipedia article. It was developed by a company called Audio Light. The suite consists of the KoalaPad which is a drawing tablet and KoalaPainter which is an accompanying art/graphics program. The program, on a C64, works with Koala format image files. The KoalaPainter program can load existing files, which you can then edit using a wide range of tools like fills, boxes, lines, ellipses, and brushes with different patterns. Then you can save your work to disk, again in the Koala image file format.
That's great! It actually sounds pretty modern when described like that. It sounds like how photoshop works. The quality of the images and assortment of tools, of course scaled for the age of the machine, but it sounds like a pretty standard modern way that a computer works. You have an application, which you load and run, it has a graphical user interface, you pick a file from a directory listing from a harddrive, it loads the file in, you work on it, save it back to disk. You can later copy the image files around individually, back them up, share with your friends, upload to the internet (or to a BBS back in the day), and so on. Very modern.
As with any image file format, especially if the files are going to be distributed, the people who receive the image are most likely not artists with intentions of manipulating the image. They are normals like us who just want to look at the beautiful artwork that has been produced by others far more talented than we are. And so to view the image files it doesn't make sense to have to load the entire Koala graphics editing program. Not to mention the fact that the original full graphics editing software likely cost money, as well it should.
What you want then is to have a free viewer that is small and quick to load, which can display the image files created by the full editor to people who just want to look at them. Again, though, this is a very modern concept. You don't have to own photoshop, nor launch photoshop, to look at an image file that was produced by it.1
I like to use a Mac to convert images (JPEGs, PNGs, etc.) to Koala format, (exactly what that format is I'll mention below.) And I also plan to have a network service which will fetch images from URLs and convert them to Koala format on–the–fly for C64s to be able to browse the web, in a more meaningful way, via proxy. A viewer is therefore far more important to me than the original KoalaPainter program. And so I found a simple koala viewer online. It's just 4 blocks (~1 KB) on disk. But… how do you use it? How does it work? Where does the modern end and the antiquity begin?
How is a Koala image formatted?
First, let's talk about the graphic formats we're all accustomed to. When you examine a JPEG, or a PNG, or a GIF, you actually find that the internal structure and layout of the data on the disk—even when they represent the same picture on screen—is radically irreconcilably different from format to format. Why is that? Well, there are proximate reasons and ultimate reasons. I like thinking in terms of the latter. The ultimate reason is because the graphics capabilities of modern video hardware long ago outpaced the increases in storage and load speed from harddisk or network. I'll explain.
A MacBook Pro, today, has a pixel resolution of 2560x1600, and each pixel can show at least 24 bits of color. 8 bits of red, 8 bits of green and 8 bits of blue for every pixel. That's 3 bytes of data per pixel. 2560 times 1600 is 4,096,000 (4 MILLION) pixels, times 3 bytes each is 12,288,000 bytes. That's 12 megabytes of raw data for just a single image that fills the screen. And we all know that many images are in fact larger than the screen and software allows the user to zoom in or out or pan around to see the whole thing. It is not at all practical or economical to actually store all those megabytes for one image.
Therefore, each of the common graphics formats, JPEG, PNG, GIF, etc. use different compression techniques (sometimes lossy) optimized for different general use cases. Each format sacrifices something, sometimes something that is difficult to perceive, in order to dramatically decrease the necessary storage requirements on disk. The task then of the viewer or the decoder, as they're more properly called now, is to uncompress the data on disk (or from a network) and reconstitute the full bitmap data in memory, whence the video hardware actually outputs the data to the screen.
And herein lies the first big difference. On a C64, 16 colors is 4 bits and 320x200 is 64,000 pixels. That would mean at least 32 kilobytes of storage for a full screen of image, but storing and manipulating color data alternately in upper and lower nybbles is a pain. So practically speaking even though 16 colors can be represented with 4 bits, if each color value is given its own byte that doubles the memory storage requirement. So, with that in mind, 64,000 pixels times one byte per pixel takes up almost 64 kilobytes in memory. But the C64 only has 64K of memory.
For this reason, the design of the VIC-II chip relieves it from needing to assign one color to each and every individual pixel. The VIC-II can show a fullscreen image, and if the artist is clever, it can be hard to notice any limitations on colors per pixel, even though there very much are. But if you think about what that means, the VIC-II chip embeds a special type of compression directly into its native display modes. The VIC-II in fact, as we all know, has several display modes, which in a sense can be thought of as multiple native compression formats. Koala makes use of the format called "Multi-Color". This is not a post about the details of how multi-color is structured, but you can read all about it and see some examples here.
The point is, only around 10 kilobytes of memory is required for the VIC-II to show a fullscreen colored image. And what is saved to disk is also very close to 10K, just a few extra bytes are in a Koala image file on disk than the raw data in memory. It's not a bad compression format at all. If you convert a 10K Koala image to PNG, it becomes 20K. If you convert to GIF, it becomes 14K. 10K is pretty good. It becomes apparent, especially if you send the file over to a PC or Mac that doesn't have the same colors–per–pixel limitations, that the on–disk format is essentially just a full bitmap with a rather unique compression scheme and cooresponding set of limitations. In fact, the Mac has no difficulty at all viewing Koala (and other) C64 image formats. You just need the right decoder. You can download and install a quicklook plugin with support for various C64 formats here. This plugin decompresses a Koala image essentially the same way another plugin decompresses a GIF, and converts it to raw bitmap data native to the Mac's video hardware.
And so the format of a Koala image is effectively just a dump of memory. The bitmap and color memory regions are not contiguous in a C64, however, so in a Koala image those three dumped regions are packed together, plus a few extra bytes. And that's it. On disk, a Koala image file is already in what amounts to a compressed format. So it's quick to load and small to store. But, it's better than that, because the viewer program doesn't actually need to do any work decompressing and converting the data to the native format of the video hardware. Because the video hardware interprets the on–disk compression scheme natively.2
This is very different than modern graphics formats. There are other multi-color mode image formats for the C64, you can read all about them and their technicals here. What is so telling is that it only takes a couple of lines to describe the difference between each of these formats. Because they are merely different arbitrary orders of appending together the various dumped regions of memory. Some put color memory first before bitmap memory, some after, some put the background color at the beginning, some at the end, some between the bitmap and color memory, etc. But large swaths of the files from two of these different formats will be exactly the same: the format of the VIC-II's multi-color mode, either bitmap or color data.
Where else do things differ?
Let's start with how you actually use the Koala Viewer that I found. The viewer is so simple it almost boggles the modern mind. It has a one-button user interface, and I mean, a one keyboard button UI. You load it, then you run it, and by default a bunch of crap displays on the screen. Nothing is responsive, there is no anything, except crap on the screen. At first, I would just reset the computer to get out of the viewer. There goes my uptime! (A concept that does not exist in the Commodore 8-bit world.) Power cycling or resetting the machine is a common user interaction model for exiting a program.
It wasn't until I disassembled the program to figure out how it worked that I discovered you can press the space bar to exit the program (or press fire on a joystick in port 1.) It's really that simple. Run the program, press space to exit the program. The end. On afterthought I should have thought to slap the long one, since that is indeed such a common interaction in games and demos that it is more or less a C64 standard. Disassembling the program also got me to figure out how it is you actually use this viewer program to, you know, view something. But I'll return to analyze the code a bit later. First, let's just pretend we knew all along how to use it.
Here's what you do. You first have to use commands from the READY prompt to load the image data into memory. Then you load and run the viewer, and it knows where to look in memory to find that image data. This explains why you just see a bunch of crap if you load the viewer but you haven't first loaded in any image data. The viewer just happily shows you whatever left over crap was in that place in memory from the last thing you ran. The VIC-II happily interprets something, anything, even executable code, as though it were image data. This is all so very very different than anything you'd find on a modern computer. It's just so low level. But it is fun, it's fun to feel yourself so close to the bare metal.3
You'll notice that even though a Koala image file is technically data, as opposed to executable code, it is stored on disk as a PRG type file. This means it can be loaded. But the C64 has two kinds of loads. You can load a PRG relocated to $0801, which is the default, or you can use a relocate flag that prevents the relocation and will instead load the data to the address specified in the first two bytes of the file on disk. That two byte header is not loaded into memory, it is loaded from disk, used to figure out where the following data should go and is discarded. Fortunately, JiffyDOS includes 4 different load commands: (/,^,% and £). Frontslash, up-arrow, percent sign, and British pound sign. These commands alone feel incredibly ancient. They are completely arbitrary one-character BASIC commands. They're so unusual, modern North American keyboards don't even have 2 of these 4 symbols on their keys. They do the following, respectively:
- Load relocated to $0801 but do not run,
- Load relocated to $0801 and run automatically,4
- Load to header–specified address but do not run, and
- Load to header–specified address and run (by jumping to wherever it was loaded.)
For Koala images, we have to use the percent sign (%) load. This puts the data in memory, but does not attempt to jump to it (thank god, because it's not executable.) Next, we load the viewer. In the screenshot above I loaded it with frontslash (/), so that I could list it to see what we can see before it runs. What we see is the standard basic preamble, SYS 2061, a BASIC command to jump to the immediately following assembly code. Pretty standard way to get assembly code to be loadable/runnable with the "/" and "^" JiffyDOS commands, or ye ol' standard LOAD"PROGRAM",8. Finally, let's run the viewer!
Oh. My. Gawd. It's Deep Space Nine!! I'm so excited. But, seriously, it's pretty cool right? It worked. After we enjoy the image for a while we slap the long one to exit the viewer. Strangely though, we're taken back to the READY prompt but something is not quite fully restored. The majority of text on the screen is white rather than light blue. Only the final READY prompt is light blue, and that's because it was drawn to screen by the KERNAL after the program exited.
It should start to feel more and more as though this entire experience is straight out of a computer era that ended decades ago. How is it even possible that we could use the JiffyDOS un-relocated load command to get the image data into memory? For that, we need to examine the file itself. It also takes time to find equivalent tools on the C64 that you might be used to having on a Mac or PC. This is not a pick–on, of course, once you start living in the Commodore world for some time, you do find these tools, and you start to know intuitively which tools you need to use to accomplish common simple tasks.
DraCopy is a great little utility released by Draco in 2009. You can download it here. Besides being a useful 2-panel file system navigator and file copier, it has a built–in HEX viewer. When you don't have a HEX viewer, and you need a HEX viewer, finally finding one is like a breath of fresh air.
And there it is. We examine ds9.koa with the HEX viewer and find that the first two bytes are $00 and $60. That's little endian, least significant byte first, for the memory address $6000. When we do the JiffyDOS un-relocated load, it is reading that address from the image file, and using it to know that it should put the image data into the fixed memory address $6000... and up.
Please, take a moment to stop and think about what this means. The image file itself has hardcoded into it a fixed memory address whither the data should be loaded. Unthinkably ancient. Can you even imagine, a JPEG file embeds a memory address that tells the computer where in memory this JPEG data ought to be loaded?! How presumptuous. How parochial and shortsighted. Why should it ever be the decision of the data file where it itself should go in memory? That data file has no idea about what else the computer might be running or using that memory for.
Why it worked this way actually makes sense, from the original KoalaPainter program's point of view. The KoalaPainter program put itself into memory, and intentionally left a space in memory where the graphics data should be loaded to. $6000 to $8710. (More on this particular location when we examine the code of the viewer.) Next, everyone who has coded anything with files on a C64 knows that the KERNAL can do a LOAD much faster than if you loop over repeated calls to CHRIN, reading one byte at a time. It was an eminently reasonable decision for KoalaPainter to save the image data as a PRG with a header address of exactly where the program wants to load the data, for itself.
But from a viewer perspective it makes no sense. The viewer program is not the original KoalaPainter program. And who knows how big it may be or what areas of memory it may occupy, and inside the context of an OS with a memory manager it is even more obscene. But it is what it is, and this viewer program is clearly hardcoded to look for the image data starting at $6000. Here's the thing though, if JiffyDOS handles loading the data into memory, and the data is already formatted as expected by a native VIC-II display mode, then what is it that a Koala Viewer actually has to do?
Digging into the code of a Koala Viewer
I manually transcribed this code to a Gist, from the photographs of an ML monitor's disassembly. All the numbers are in HEX. This isn't typically the style I would use when writing code, and labels are not used, rather hard memory addresses are used because that's how the disassembler produced the output.
Obviously I added the comments. I'm not super familiar with VIC-II programming, so why it has to mess with the VIC's raster interrupt, I truly do not know. Here's the code, let's dig in.
The code consists of three main parts. The first part, lines 1 to 44 are the "main" program. The second part is a subroutine called by the main program to configure the VIC and CIA2, and move data from where it was loaded in memory to places the VIC can use it. And the last part is a short routine wedged into the KERNAL's IRQ service routine, which scans the keyboard and allows the main program to progress past viewing the image, to the clean up part and exit.
So, main program, in detail. Mask CPU interrupts so we don't get disturbed while setting up memory and the VIC. Then it also masks CIA1's interrupt generator, not 100% sure why this is necessary since the CPU is already ignoring interrupts.
Lines 8 to 11 are what wedge this program's IRQ routine into the KERNAL's IRQ service routine. The KERNAL hops through a vector at $0314/$0315.
Next, it turns off the VIC's display. This causes the screen to blank out. I believe that's because it will look cleaner to move all the graphics and color data into place while the screen is off. And the screen can be turned back on when everything is ready to go. At this time the border color is also set to black. The border color must not be specified by the Koala image format. That's a shame, if you ask me, it's an important part of the image. Especially if you wanted the edges of the image to blend seamlessly into the border.
At line 17, the main program calls the only proper subroutine. So let's go check out what that subroutine does.
The first thing it does, from lines 50 to 72, is copy 2 kilobytes of data. The way it does this is very cool. It actually loops just 256 times, one complete cycle of the X register from $00 to $00 counting backwards. On each loop, it copies 8 regions in parallel. The first 4 regions are the 1K (256 * 4 = 1024 bytes) of Koala data into Color Memory. The second 4 regions are 1K that are copied from Koala data into "Screen" Matrix Memory. In the Multi-Color mode, Screen Matrix Memory is used to hold the extended color data. Both upper and lower nybbles are used to hold 2 additional colors per 8x8 pixel cell.
Interestingly, that's all the memory that is ever copied by this program. What about the 8 kilobytes of bitmap data? Now it becomes clear to us why the Koala image format, as insane as it seems, specifies where it should be loaded in memory. It's loaded to $6000 you'll recall. Let's dig into the VIC for a second.
The VIC-II chip has 14 address lines, not 16. 14 bits can address from $0000 to $3FFF, or 0 to 16383. So, the VIC can see 16K of memory at a time. The most significant 2 bits of addressing are supplied by the CIA2's Port A bit0 and bit1. This means that configuring CIA2 allows you to choose which of 4 blocks of 16K the VIC II "sees." In the C64's main addressing space those ranges are:
- $0000 - $3FFF (CIA2's $11)
- $4000 - $7FFF (CIA2's $10)
- $8000 - $BFFF (CIA2's $01)
- $C000 - $FFFF (CIA2's $00)5
Within a 16K block, there are two 8K chunks. From the VICs perspective an upper 8K and a lower 8K. Bitmap data is just shy of 8K (8 * 1000 = 8000, but 8K is 8 * 1024 = 8192). The VIC can be configured to read bitmap data out of either of these 8K regions, upper or lower, but always aligned to these two regions. It cannot for example arbitrarily read an 8K bitmap that's shifted by just 2K or something like that. If you divide the 2nd bank in half, the lower 8K goes from $4000 to $5FFF, and the upper 8K goes from $6000 to $7FFF. Bingo. $6000 is the start of an 8K bank whence the VIC-II can directly read a bitmap.
So, what's neat about loading a Koala, is that the JiffyDOS (%)-command, (load to the header address,) without any other viewer or decoder program's involvement, literally loads data directly off the disk and straight into video memory exactly where it needs to be for the VIC to display it. Crazy low level. But, hey, it's hard to imagine how it could be more efficient.
Moving on now.
Lines 74 to 94. The VIC's background color is set, which is just one byte from the Koala data into the VIC's background color register. Next, in short order, the VIC's raster interrupt is masked (or unmasked?) I'm not sure what this is for. The byte inside the infinite loop code in the main program is set so the loop will loop, not sure why this had to be done here and not in the main part of the program itself. CIA1's interrupts are, started? Or touched, not sure why this has to be done. Then the VIC's raster counter is set. Next the VIC's memory pointers are configured. This is what tells the VIC to get the bitmap from the upper 8K, and where in the lower 8K screen memory (1K, used in this mode for extended color info) should be found. And the write to DD00 is CIA2, which configures which of the four 16K banks the VIC should see. Lastly, multi-color mode is turned on and the subroutine ends, returning to the main program.
Back in the main program, now that all the color data has been moved into place and the VIC's registers have been configured, the VIC's display is reenabled. And lo and behold the image appears.
CPU interrupts are unmasked, and at lines 24 and 25 the main program just goes into a hard infinite loop. This to me is one of the things that makes this program—and the C64 and its lack of modern OS—feel the absolute oldest. Literally, the program goes into an infinite loop that is just 4 bytes long, 2 opcodes and 2 data bytes. It loads an immediate value of #01 into the accumulator. Then if the value loaded is not zero, which, duh it's not, it branches right back into loading the accumulator again. Argh! While you look at the image, that beautiful 1 Mhz CPU spins like an insane idiot doing absolutely nothing. Almost nothing. Modern computers, and C64 OS will never do absolutely nothing like this.
Almost but not quite nothing. Because there is still the interrupt service routine. So let's look at that. While the main program is in a tight infinite loop, the routine at line 100 to 110 is being called 60 times a second. The first thing it does is clear the VIC's raster interrupt. I'm not super familiar with raster interrupts, but actually, it looks like the CIA1 might not be the one generating the interrupts this time. It's the VIC's raster that generates the interrupts. Not sure why they did it this way. But in any case, reading from DC01 checks the column that the space key, and the control port 1's fire button is in. You can read my recent post about How the C64 Keyboard Works for more detail on why this works.
If the space key is not held down, it skips over the next two lines and returns into the KERNAL's usual IRQ routine. However, if space key is held down, it writes a #00 directly into $0832, that's the address that holds the immediate argument inside the main program's infinite loop! Thus, it breaks the loop and allows the main program to proceed into its second half.
The main program, following the infinite loop, is from lines 27 to 44. Everything is done between a pair of SEI/CLI, so that interrupts are masked at the start and unmasked at the end. Then it does three quick subroutine calls into the KERNAL. These initialize the SID, CIA, VIC and IRQ, and reinitialize the I/O vectors (which unwedges the IRQ subroutine previously discussed.)
The only problem with returning to BASIC immediately, is that the Multi-Color Mode uses color memory for part of the image's color map. But when the VIC is put back into character mode that color memory is used for the colors of the characters. So color memory needs to be reset. But, what to reset it to? In this case, a short loop (again in parallel, 4 regions are being set per loop,) color memory is filled with #01... white. And that explains why all the characters on the screen go white after the program returns to BASIC. The reason is because color memory is shared between character mode and bitmap mode, and when the Koala data is copied into color memory, it clobbers whatever was in there. Then it picks white arbitrarily to put back in. It could put light blue back in and most people wouldn't notice, but what would be even better is if it made a backup of color memory, and then restored from that backup.
So that's the whole process, in as deep detail as I care to go.
Some Thoughts About Modernization
Inside the context of an OS, memory cannot just be clobbered arbitrarily. It just can't be. What if the ethernet driver has allocated some space to buffer some incoming data, and you don't even know where those allocations have come from? Memory in the middle of the computer, especially around $6000 cannot just be blindly filled up with data that is loaded straight from disk. Even if that space were available, you'd need to at least mark it now as occupied, so that future allocations don't come out of it.
The VIC-II shares memory with main system memory. And a bitmap can only be in one of 8 possible locations. In my mind, the ideal place for bitmap data is under the KERNAL ROM. The 4th 16K block that the VIC can see goes from $C000 to $FFFF. That range, in C64 OS, is never allocated by the memory manager. It's too messy to use for arbitrary purposes. $C000 to $CFFF is used for C64 OS code, so that's never available. $D000 to $DFFF is under I/O, and is used in precision ways by C64 OS for storing system related data, which it accesses by patching out I/O when it needs to. And in C64 OS the KERNAL ROM is used and usually patched in, which covers the remaining 8K, $E000 to $FFFF.
However, the VIC doesn't always see what the CPU sees. Even when the KERNAL ROM is patched in for the CPU, when the CPU writes to $E000-$FFFF those writes go into the underlying RAM. And when the VIC reads from that range, it always reads from RAM. That's so great. That means with the KERNAL ROM patched in, code executing within that range can write data into that same range and can be affecting what the VIC is showing in realtime. As long as your code needs the KERNAL, and also wants to show bitmap data, storing the bitmap data under the KERNAL sounds like a no brainer.
If bitmap data is the 8K block under the KERNAL, then screen matrix memory can be configured to be found somewhere inside the 8K block from $C000 to $DFFF. In C64 OS, the layout of $D000 to $DFFF is managed manually, much the way the system workspace is managed manually from $0000 to $07FF. A thousand locations can simply be reserved under I/O space, just for extended color info for multi color bitmaps. No need to dynamically allocate it, that's just where it goes.
What does this mean for Koala image files? Or all the other multi-color format variants? Like, Advanced Art Studio, Amica Paint, CDU-Paint, Draz Paint, etc. When reading these from disk, they should be read more along the lines of how C Structures are read into memory. The headers on a file are prescribed sizes. You declare a structure composed of a certain number of bytes, and you allocate space for that structure. Then you populate the structure by loading a fixed number of bytes from the file into the memory allocated for the structure. And then, you can access all the properties of the struct to know more about the file contents.
A loader for Koala would start by loading 8000 bytes into $E000. Then another load operation would load 1000 bytes into where screen matrix memory will be assigned in $Dxxx. Then, if you want to have color memory preserved, perhaps another 1000 byte block under $Dxxx should be managed by C64 OS as the color memory buffer. But the point is that you have to load the data in chunks.
It is much faster to use the KERNAL's LOAD routine rather than looping over CHRIN and putting the bytes where you want them to go. It's also easier to use, it requires less code. However, LOAD is brutally uncontrolled. It must load the entire file, and it must all go to one contiguous range of memory. But that's just not going to cut it.
C64 OS has a File module, which not only works with C64 OS File References, but also offers the standard FOPEN, FREAD, FWRITE and FCLOSE. This makes it very easy to load in chunks. You call FOPEN with a pointer to a File Reference (which will get created for you using the C64 OS file manager,) then you call FREAD with a pointer to the open File Reference, a pointer to where the data should go, and a 16-bit length to read. And that's all you have to do. FREAD will put the data in the buffer. It handles errors, it handles looping, it handles hiding the mouse cursor and other implementational details.
Will FREAD be slower than the KERNAL's LOAD? Yes, by definition it has to be, as it uses the KERNAL under the hood. But, on the other hand, we're not loading from a 1541 anymore, (hopefully.) We're loading from a CMD-HD or an SD2IEC, or better yet from an IDE64 or a 1541 Ultimate.
More Sophisticated Data Structures
All of these image formats, (20 of them hires and multi-color, not counting the interlaced and more advanced formats) they all just assume the image is exactly 320x200. And that nothing whatsoever will appear on the screen at the same time as the image.
Storing data in a format native to the VIC-II is fine. But, what about images that are bigger than the 320x200? What about images that are smaller? Smaller images make a lot of sense to me, especially if you've got a background scene, say, already loaded, and you want to load in a small rectangular area to overlay on top of the scene. I can imagine tonnes of uses for this in games, or animation sequences or presentation software. Or even just to overlay some UI controls on top of a bitmapped image. Instead of having no UI, but to slap the space bar, in C64 OS the mouse will be active, what about being able to load in a "close" X, or back and forward arrows and display them on the bitmap screen? They could be loaded directly from disk when you want to show them. The possibilities are endless.
A more modern C64 image format doesn't need to abandon the idea that the data is in a VIC-II friendly format. But what it should have is a standard header that can be loaded first into a struct somewhere. That could specify the dimensions of the image data in 8x8 cells, the data format (hires or multi-color), and the offset into the file for the color and bitmaps sections. It could even specify where on the screen it ought to display, if that made sense. But it wouldn't do that with hardcoded memory addresses, but instead with offsets from a virtual top left corner.
Going into a tight infinite loop as your program's main behavior can't be how it works. I understand why this viewer does that. I mean, what else is it supposed to do? Where else ought the CPU to execute? It's got to execute something, and with the image statically displaying on screen, and the IRQ routine checking for the space bar, there isn't really anything else to do.
But, even though C64 OS isn't "multi-tasking", it's still got system level code that is capable of processing events. After setting up the image to display, in C64 OS, your code would just return. And it's the OS's own main event loop that will do the infinite looping. Meanwhile maintaining the ability to respond to timers, pass mouse and key command events to your code, process incoming network data, and whatever else gets added in future versions of the OS.
Okay, that was long. But it's good to go into this stuff. Leave your thoughts in the comments section.
- It's not a perfect analogy because modern image editors store extra data besides the final image. Such as layers, groups, tags and selections. So they export a "flattened" version of the image. Whereas Koala edits the same flat file that is ultimately viewed. But, it doesn't affect my argument much. [↩]
- That said, there are some formats that are compressed on disk further. Such as RLE compressed Koala format. These require the viewer to use a decompression routine. However, such schemes are very primitive. [↩]
- I wouldn't even know where to begin to actually put data, directly, on to the screen on a Mac. Most nerds would ask, "Why would you ever want to do that?" Just out of curiousity. My guess is that you have to be some sort of super privileged low level OS process to be able to put things directly into video memory. It's so arcane it seems that no one really knows how to do it. [↩]
- Note, of course, that these "relocations" don't modify the code. If the code is not intentionally designed to be runnable from $0801 it will crash. The relocate is a simple shift of position in memory. Real code relocation is much more complicated. Although it is possible it requires a special binary file format and a special assembler. See here, for example. [↩]
- It tripped me up for a minute, because the program writes #$02 to DD00, to me, that looks like it should be the 3rd 16K bank, 00, 01, 10, 11. 0,1,2,3, right? But I checked the C64 schematics, and indeed, the V14, V15 address lines coming off the CIA2 have a bar above them. That means they're inverted, for whatever reason. So the correct order is 11,10,01,00 for banks 0,1,2,3. Thus #$02 really does give you the second bank not the 3rd. [↩]