NEWS, EDITORIALS, REFERENCE
Rethinking the Memory Map
I've had my head down, programming away on C64 OS, and been making some great progress. I've tweeted a few things out, some of which are revealing of things I've either never talked about here or even seem to contradict what I've said here. So I'd like to take a breather and talk about some of these developments.
When I started working on C64 OS, I was really just beginning to learn to program in 6502. I'd had a bit of experience from the past, but nothing extensive. I extended the digital audio driver in WiNGs from mono to stereo, for example. But that only involved a handful of lines of code. I played around a bit back in the day with some demo techniques, like making a raster bar. Demo coding always felt like too much work to produce something that would be a mere demonstration of some visual capability of the machine. And I plinked around with Geos and Wheels coding after getting a SuperCPU, but before falling headlong in love with WiNGs. As I've said before though, most of my WiNGs coding was done in C. And the vast resources, standard C memory management coupled with many megabytes of ram, meant that coding for WiNGs kind of felt like coding for an old PC.
This time it's different. I can now say with confidence that I'm a 6502 programmer. I don't know all the tricks yet, there is a lot to learn, especially from experience, but I know how the game is played now, and I'm knee deep in the mud.
One of the most shocking and difficult-to-get-used-to qualities of the C64, (besides low screen resolution, low clock speed and slow disk I/O) is the sheer lack of ram. 64K may have seemed big in 1982, but today 64 megabytes feels impossibly small. The small size of 64 kilobytes is hard to even fathom. To help visualize it, I just whipped up this image, 256x256, with grid lines every 16 pixels, and 16 subdivisions, one for each pixel. Then I zoomed in enough so you can see the grid lines.
Here it is:
There it is, that's 64 kilobytes. Each square, surrounded by the inner grid lines, is one byte. You can literally see and discern with your own eyes every byte in the whole memory space, all at the same time. For one more point of reference, who knows how long this post will be, but my previous post, Key Map to Screen Editor, without the images, just the text with some light HTML markup, is 60 kilobytes.
When you know that's how little you have to work with total, you have to get over the minor heart attack. Profesionally, I've worked on web-based business apps, where sometimes just one class is over 100K. How can you put an entire operating system, with keyboard and mouse drivers, memory management, string and math libraries, an event model, widget toolkit, menu system, screen compositor, and more, in just a handful of kilobytes such that you'll still have most of the small space left over for an application's own code and data? If you think about it too much, it becomes paralyzing.
At the outset of development, I was obsessed with how to conserve as much memory (and CPU cycles) as humanly possible. If I could use bitflags instead of whole bytes, that's what I thought I should do. If I could hide some non-code data beneath a rom, that's what I thought I would try. As I got a bit more familiar with coding, and what the 6502 instructions and addressing modes can do, it became apparent that some of these tricks are worth it, and some of them are not.
Such as was discussed in this post, Memory Manager Development, especially under the heading False efficiences. After some effort of writing a paged memory manager that would use bits to indicate used pages of memory, and then cramming a 32 byte bit-map under I/O space at $Dxxx, I realized you need more code to manipulate those bits than the number of bytes you save by using bits. This was a big early mistake that I reversed. After switching to using full bytes for each page other advantages popped up, besides just how easy it was to work with them.
For example, I can now use different values of bytes to represent how a page was allocated. Whether the system allocated it, or the App allocated it, whether it holds Application executable code, or application data, and eventually perhaps network allocated, or weakly allocated, etc. Having types of allocations was not possible when it was either 0 for free or 1 for allocated.
In retrospect, it really was most apt to call the earlier approach a false efficiency.
Another false efficiency
More recently I have come to realize another false efficiency that I had very early latched on to. But this one is a very different kind.
Way back in late December of 2016 I wrote this post called A Modern Character Set. The gist of the post is the fact that PETSCII is missing eight characters that are very common on the internet today. These are: backslash, caret, underscore, back tick, pipe, left and right braces, and tilde. If your built in character set is missing these symbols then there are only a couple of options. One option is to use a custom character set, to which you can add the missing symbols.
The problem with this option is as follows: You have 256 symbols (glyphs) in a ROM. The ROM is visible to the VIC-II video chip, even when it is not visible to the CPU. This means wherever the ROM sits, it doesn't take up 2 or 4K of memory addressing space, it takes up no addressing space at all. That's great. But, if you want to have 8 custom glyphs, or even just one custom glyph, you have to copy the entire characterset (2K of the 4K ROM) into ram. Then you can modify one or two or eight, or every character. But if you only want to modify 8, you've got another 248 glyphs in memory that are exactly the same as they were in ROM, only now they take up precious ram.
Another option is to try to make use of some of the PETSCII graphics symbols to stand in as equivalents of some of those other missing characters.
And this is what I "settled" on doing. Here is what my conclusion was from that post:
So, where does this leave us? By mapping common characters found on the internet to some shifted set PETSCII graphics characters, we can take eight problematic characters and deal nicely with seven of them. The 8th is uncommon and not necessary even to produce. Sticking with this technique, we can save nearly 2 kilobytes of RAM. A little less than 2K savings, because the map of these characters and some logic in PET2SCR will be have to be written. But overall a huge memory saving.
In after thought, this has turned out to be a mistake. Let me tell you why.
Beauty, in the eye of the beholder?
Over the months, I've worked on many esoteric underpinnings of the OS. The file references and routines, building out the memory manager, the app loader, and only more recently have I gotten deeper into some of the user-facing parts, the menu system, the toolkit, etc. As I developed, I realized there were other things besides just 8 missing ASCII characters. For example, I've implemented the keyboard shortcuts in the menu system. The keyboard modifier keys are C=, Control and Shift. But, how to represent the concepts of C=, Control and Shift with single characters? I had to opt for abstractions here to.
The result was this:
It's not perfect, it's a bit abstract, but you'd get used to it right? And, come on?! We're still saving 2K of memory by sticking with the character set in ROM. Am I right? Am I right? Well, let's see.
A few months ago, someone from Germany wrote me to ask about the potential for a collaboration; my C64 OS underpinnings with his graphical UI. He sent me some screenshots of his work. I don't know if the work is private, obviously, he holds the copyright on his own productions. I don't want to violate any copyright boundaries, but I'll present to you here just one of the screenshots he sent me, under the freedom of being able to critique something.
Needless to say, it looks really great. I think you'll agree.
I looked over his mockups, and one thing struck me most profoundly: Oh Shit, I forgot. This stuff actually has to look good! Or no one is going to want to use it.
Bitmaps vs Text
There is one overarching principle, from which I am not (yet?) prepared to budge. C64 OS has to feel fast. Speed of interacting with a computer is like 95% of what it takes to making a system usable and modern. I am almost ready to shoot another video, to accompany a new post I'll start working on soon that compares and contrasts the menu system and attendant keyboard shortcuts in C64 OS with those in Geos/Wheels. When I post that video, I think you'll agree that the difference is quite stark.
The bottom line is that rendering to bitmaps on a C64 is very slow. Alternatively, rendering to text is very fast. Game developers have known this, basically forever. That's why all the zippy fast games are actually done in a text mode, augmented with sprites. On the other hand, these games don't all look like they've been coded in PETSCII art either. What is the happy middle ground? The game devs figured this out a long time ago too. The happy middle ground is a custom character set.
As totally rad as those mockups from our German friend look, they are ultimately antithetical to the approach I'm taking with C64 OS. But, they are a bit of a kick in the pants to remind me that if you want anyone to appreciate your work, it has to not just be speedy, but it has to look good too.
An operating system is somewhat different than a game. A game has a very controlled progression through certain stages. For instance, during game play you might not need to show the full alphabet of characters, but during a cut scene, or between levels you may want to show a block of text. It is actually possible to have two character sets coded up and stored in memory, each for different phases of the game. And because the phases are so controlled, you can choose which set to use during each phase.
An operating system is inherently more text-centric. You have to have access to a complete, and by complete I mean all the ASCII characters, set of characters, upper and lower case, at all times. If you refer to my programming reference document, Commodore 64 Screen Codes, you'll see that we have 8 blocks total. But the upper 4 are the reverse of the lower 4. So we have 4 effective blocks. The first and third are alphabetic. The second is symbolic. And the fourth is graphical. We're going to need both upper and lower case letters. And we're going to need all the punctuation, numbers and symbols, plus the 8 missing ASCII characters overwrite/replace some of these. This leaves more or less the entire 32 characters of block 4 to be customized for various graphical ends.
After consulting with a local graphic design friend, Brad Marshall (more about him), I became 100% convinced that using a custom character set, 2K be damned, will be essential for getting the look that I need. I'm not going to go into full detail here about every way in which I'll be able to make use of a custom character set, but I will present you with a few images to show some of it in use.
Here are two screenshots, one is an actual photo of my 1084s monitor while C64 OS is booting up on my C128. The other is a mockup I made on a Mac to play around with what it might look like to have certain custom characters available to the UI.1
Clearly, I'm not arguing that my screenshots are anywhere near as graphically rich as those presented in the full bitmapped mockup. However, they do show a number of great possibilities, more of which I'll be exploring in upcoming posts.
The booter loads in its own character set, which provides a set of 24 characters that can be tiled together as an 8x3 (64px by 24px) logo. Additionally, it can do things like put rounded corners on the box, and a copyright symbol in the footer message. One of the last things the booter does is loads in the character set that will be used by the operating system, beyond the booter. This set can replace those logo characters with characters dedicated to drawing certain UI elements. And I'm toying with an idea for standard system routines to make it easy to sub in replacements for just those 32 characters in block 4, which can automatically produce their reverse variants for block 8.
In the second screenshot, you can see the menus no longer need to use abstract symbols for the keyboard modifiers. Instead of an asterisk to mean the Commodore key, it can be a Commodore symbol. Once that symbol is in the set, it can be used anywhere else in the UI. The Caret symbol was one of our 8 missing ASCII characters. Now it can play double duty. It can be used in ordinary text, but it can also be used in the menus as the "control" symbol, as it is commonly symbolized in Linux, Unix and macOS. And an up arrow, part of a set of directional arrows that can be used for scroll bar buttons, can also be used in the menus. The up arrow commonly symbolizes "shift", much more than my suggested front slash. And the right pointing arrow can be used to indicate that a menu item has a submenu.
Additionally, there is room for some other characters you might not expect, such as the horizontal stripes on the top of that color picker panel. These can be used to indicate that the panel can be dragged about the screen. I'm working on coming up with a set of these characters that can most universally be used to construct a UI.
Where to find 2K?
Okay, so, as you'll recall, I originally resisted creating a custom character set because it requires occupying at least 2K of ram that could otherwise be used for useful program code or data. Ram usage in the C64 is a bit complicated, and although information about it can be found all over the web, I'll summarize the relevant complications here.
The C64's CPU, the 6510, has a 16 bit address bus which provides it with an addressing space of 65536 "spots." It also has an 8 bit data bus, which means that each "spot" is 8 bits, or 1 byte wide. Put the two together and the 6510 can address 65536 bytes or 64 kilobytes worth of stuff.
In theory though, that stuff need not all be RAM. Some of that stuff could be ROM, and some of that stuff could be I/O chips like the VIC, SID and CIAs, or other I/O chips in a cartridge in the expansion port. In fact, this is how it is on a simpler machine like the VIC-20. The VIC-20 has 5K of ram, but it also has a combo Video/Audio chip, and some variety of CIA chip, plus it has its own KERNAL and BASIC roms. Because the 5K of ram is just a small part of its 64K of address space, everything fits in at the same time.
The C64, though, has a full 64K of ram, PLUS it has 4 I/O chips, extra room for I/O on the expansion port, a special 0.5K of static color ram, and 3 ROM chips, KERNAL, BASIC and Character Generator. How does it fit that all in to the same 64K address space? Good question, but one that has been generally known even by ordinary C64 users for a very long time.
The 6510 CPU has a special port2, which is a set of pins whose input/output status and high/low status can be affected directly by whatever values are written to addresses $0000 and $0001 respectively. Three of these pins are connected to the PLA, which, together with a couple of off-the-shelf 74xx binary decoders, is a sort of complex address router. Depending on the configuration of those three pins, the PLA will disable regions of ram, and enable alternative resources that are in that same address range. 3
The three address ranges are:
$A000—$BFFF | BASIC ROM |
$D000—$DFFF | I/O or CHARACTER ROM |
$E000—$FFFF | KERNAL ROM |
Each of these three regions may be either RAM, or the alternative. There are some limitations of combinations that are imposed by the PLA. For example, the BASIC rom is essentially a high level front end to routines found in the KERNAL. For this reason, it makes no sense to ever have the BASIC rom patched in while the KERNAL rom is patched out. Such a combination would only ever lead to a crash.
Here's a great summary, with brilliant visuals that I'm too lazy to produce myself, in an article over at dustlayer.com: RAM UNDER ROM - A BRIEF LOOK INTO C64 MEMORY.
A few more tidbits must be known to understand what we've got to work with. When I/O is turned on, the so called "I/O" chips are enabled from $D000–$DFFF. Interestingly though, one of those I/O chips is actually another ram chip. It's a static ram chip which occupies a 1K range of address space. Interestingly, it itself is not 8 bits wide. It's only 4 bits wide. So it technically only stores 0.5K of data, even though it occupies 1024 addresses on the CPU's address bus. Confusing, eh? So, whatever you write to those addresses, when I/O is patched in, the upper 4 bits are not preserved by anything, only the lower 4 bits actually get set inside that ram chip.
A brief aside:
You can test this out from BASIC, just for fun. POKE 55296,200. That 55296 is $D800 in HEX, the
first location of that I/O mapped static ram. Now do this: PRINT PEEK(55296). Lo and behold, you
get back a value of 8. That's because 200 in binary is this %1100 1000. But the upper nybble %1100
is not saved, only the lower nybble %1000, which in decimal is, you guessed it, 8. Pretty cool.
What is this odd 4-bit-wide static ram used for? It's used by the VIC-II to store color data.
And since the C64 only has 16 colors, those neatly fit into the 4 bits available at each address.
This leads us to the next interesting feature of the C64 and its memory map. The VIC-II and the 6510 share both the address bus and the data bus. The VIC-II is accessing the same RAM, over the same address and data buses, as the CPU, but it's not quite that simple. The VIC-II's own address lines are not wired up to the address bus via the same address-routing PLA as the CPU is. The VIC-II has its own world of subtle complication which must be understood before we can figure out what to do with our 2K of ram for a custom character set. The character set has to be available to the VIC-II, it can't just be thrown into any contiguous 2 kilobytes of memory without regard to how the VIC-II works with the buses and the address space.
From the VIC-II's perspective
The VIC-II doesn't have 16 address lines, it has only 14. (I go into a bit of detail on this in my post, Anatomy of a Koala Viewer.) If you plug fourteen 1's into a hexadecimal calculator and convert that to decimal, you'll see that the max value of 14 bits is 16383, that's 16K or one fourth of 64K. Whenever the VIC-II is accessing the bus, it itself is setting the address, whence to read, on the lower 14 bits of the bus. Meanwhile the most significant 2 bits are being supplied by two pins on CIA 2.
Therefore, the VIC-II is only ever seeing 16K, or one fourth, of the total 64K of ram at a time. This is important, because from the CPU's perspective you could put your screen matrix data way down low in memory, and then put your custom character set somewhere in the middle and put your mouse cursor sprites up high in memory. But it wouldn't actually work to do that. Everything that the VIC-II must display on the screen at the same time has to all be available inside the same 16K block of memory.
We're not through with complicated things to wrap our heads around yet though. Just as the CPU has some special address routing going on, the VIC-II also has its own variety of special address routing, beyond just which 16K block it's looking at. The first is that, no matter which 16K block the VIC-II sees, that is, no matter which combination of upper 2 bits you set on CIA 2, the VIC-II always has access to that static ram chip for color. From the CPU's perspective that color ram chip is mapped in as another kind of I/O. You map in the VIC and the SID and the CIAs, oh and in comes the Color Ram too. But, from the VIC-II's perspective, that color ram chip is not part of the 16K bank of main ram that it can see.
I just learned this, so it's new and exciting to me.
The color ram is special, the vic always sees it on an extra bus groepaz__, #c64friends, March 21, 2018
Indeed, when you look at the schematics, you can see that the VIC-II has not just 8 data bus lines, D0 through D7. It has 4 additional data lines, D8, D9, D10 and D11. These are wired directly to the 4 data bit lines of the static color ram. This means the VIC-II is able to read 12 bits of data simultaneously, on the same clock cycle. It also means that it has access to the color data without needing to punch a 1K sized addressing hole somewhere in the 16K bank of main memory it's able to see. That's very cool.
Now, what about the Character ROM?
The CPU essentially never needs to have access to the Character ROM. If you already have a complete character set in a file on disk, you can just load it into ram somewhere and change the VIC-II's configuration to use it. The only reason the CPU ever needs to see the Character ROM then is for the convenience of being able to copy its contents to ram, where it can be modified, rather than loading the whole set from disk.
The PLA therefore, again configured via the processor's port, can be set to patch in the Character ROM. It gets patched in at $D000-$DFFF, overtop of where I/O would otherwise be available. But again, the CPU rarely needs to ever see the Character ROM. It's the VIC-II that needs to know where to get the character data.
So, the VIC-II is set to see one of the four 16K banks. These are numbered Bank 0, 1, 2 and 3. When the VIC-II is looking at Banks 0 and 2, it will always see the Character ROM, from its own perspective, in the address range $1000 to $1FFF. Because the CPU only sees that ROM when it is explicitly patched in, the CPU and the VIC-II are looking at only partially overlapping content within the same range. By default, the C64 starts up and the VIC-II is looking at Bank 0. Screen memory is from $0400-$07FF, that's in the VIC-II's Bank 0, and it's using the default Character ROM which is at $1000-$1FFF, but only for the VIC-II. Clearly, for the CPU, $1000 and up is just regular ram being used for your BASIC program.
Even when the VIC-II is seeing Bank 0 or Bank 2, where it could in theory look at the Character ROM, it can still be configured to look at, say, $2000-$2FFF instead, in which case it would be looking for character data out of main ram. And when the VIC-II is set to use Bank 1 or 3, then no matter where it looks, it always sees main ram. Here's how the two maps overlap, including the cool side effect that the little block of color ram which can be seen at $D800-$DBFF by the CPU when I/O is mapped in, is completely outside the 16K main address space when seen from the VIC-II's perspective.
Whoops, damn. This drawing kinda shows the memory upside down. Sorry about that.
Here's something though. If you put the VIC-II into Bank 3, it will always see main ram, regardless of how the PLA configures what the CPU will see. This has very interesting repercussions.
Recall from the top of this post, I'd always intended to use the ram under I/O as a special storage place for system variables. Things that aren't code, because it's very difficult to organize when I/O gets patched out and what resources code in ram under I/O can access. The problem was, I never got beyond just putting the memory map under there. This was never more than about 150 bytes, and then I ran out of useful things to stash under there. Although, I had toyed a bit with the idea of a screen buffer, to speed up and smooth out composites of multiple layers. You can see that outlined in this post, Memory Manager Development, under the heading, The C64's Memory Map, Under C64 OS.
One other thing occurred to me. I'd always assumed that if I wanted to show a bitmapped screen, I'd use Bank 3, because the VIC-II can see the ram under the KERNAL, even while the KERNAL is still visible to the CPU. One problem with this, is that we'd need a space for the color memory, also in Bank 3. And we'd also have the problem that the mouse sprites are located in Bank 0. So, would the mouse have to become disabled when viewing a bitmap? That's a shame, I'd always imagined using the mouse to click some overlay controls like these ones from macOS:
Furthermore, if screen matrix memory is in Bank 0, but the Character ROM is visible to the VIC-II in Bank 0 from $1000 to $1FFF, then I wouldn't be able to put a custom character set there. I'd have to move the custom set to $2000-$27FF, but that's even worse than consuming 2K of memory, because it would split into two isolated chunks what is ideally a large contiguous block of memory for application code and data. ($0800-$1FFF, $2800-$A8xx or wherever C64 OS kernal code ends).
And then the solution dawned on me. All the pieces fell into place like a well designed puzzle. It's like it was there all along, just waiting to be discovered.
C64 OS's new Memory Map
Here's what I've done, and it seems to work very very well. It actually solves problems it wasn't expected to solve, and total contiguous free memory for the application and its data goes up!!
The VIC-II is pointed to Bank 3. The lowest 4K ($0000-$0FFF from the VIC-II's perspective, or $C000-$CFFF from the CPU's perspective) is occupied by C64 OS KERNAL Code, so nothing can go there.
However, the next 4K, ($2000-$2FFF for the VIC-II, $D000-$DFFF for the CPU), has very interesting properties. It's usually set as I/O for the CPU, so we can talk to the SID to play background music or play a sound effect, or scan the mouse. Read the CIA's to get the current time, or scan the keyboard, or get data off a WiFi modem. Write to the VIC to change the position of the mouse or other display sprites. But meanwhile, when the CPU wants to have ready access to I/O, the VIC-II will happily see the underlying ram at the same time.
I only need one character set, not two. So the first half of this 4K range is the 2K custom character set. The next 1K is a color memory buffer (more on this in a second.) And the last 1K is the screen matrix memory.
The last 8K block, which for the CPU is usually occupied by the KERNAL ROM, will be for bitmaps. Here's the thing though. Bitmap data does not occupy a full 8K. It occupies 8bytes x 1000 locations, or 8000 bytes. 8K is 8192 bytes. The last 192 bytes are free. That 192 bytes divides evenly into three chunks of 64. A sprite definition needs exactly 64 bytes. This gives us the perfect spot for, theoretically up to 3 sprites, but more practically, our two mouse pointer sprites with 64 bytes remaining for additional system variables.
Jbevren, on IRC, once pointed out to me that when you patch out the KERNAL ROM, the CPU's NMI, IRQ and RESET vectors are then read from ram instead. Usually one disables interrupts before patching out the KERNAL. But another technique is to put custom IRQ, NMI and RESET vectors into the underlying ram. These custom vectors can point to routines that patch the KERNAL back in and then jump through your normal IRQ handler, which may make use of the KERNAL, and then just patch the KERNAL back out before doing the RTI. Brilliant. And the extra 64 bytes of unused space after the mouse pointers leave such a possibility open.
Here's how the new memory map ends up looking:
The 6510's Perspective The VIC-II's Perspective * +--------------------------------------------+ +--------------------------------------------+ | $E000 - $FFFF - KERNAL ROM 8K | | $2000 - $3FFF - BITMAP SCREEN BUFFER 8K | | - BITMAP SCREEN BUFFER | | - 2 SPRITES | | - MUSIC BUFFER | | | | - UNSTRUCTURED FREE SPACE | | | +--------------------------------------------+ +--------------------------------------------+ | $D000 - $DFFF - I/O, CHAR SET 4K | | $1000 - $1FFF - CHAR SET 4K | | - COLOR BUFFER, SCRN BUFFER | | - SCREEN MATRIX | +--------------------------------------------+ +--------------------------------------------+ | $B000 - $CFFF - C64 OS KERNAL 8K | | $0000 - $0FFF - C64 OS KERNAL 4K | | | | | | | +--------------------------------------------+ | | Lower three banks, not visible. +--------------------------------------------+ | $0500 - $AFFF - PAGE ALLOCATED SPACE 42K | * Updated, thanks to comment poster zu03776. | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +--------------------------------------------+ | $0000 - $04FF - SYSTEM, STACK, PG MMAP <2K | +--------------------------------------------+
This is nearly perfect. It solves several problems:
The mouse pointer sprites and screen memory are now in the same Bank as the memory under the KERNAL ROM where bitmap screens will go. So, using the mouse over a bitmap screen will be easy and flipping back and forth between a bitmapped screen and the C64 OS standard text-mode UI will also be easy.
We get our custom character set, and it's tucked away under I/O that is hard to use for any other purpose.
Screen memory is now in the ram that's hidden under I/O though too. Is this going to cause a problem? I don't think so. In C64 OS, an application should never just write directly to the screen. Each application has to implement a draw routine, which is capable of redrawing the entire UI at any time. It is the main event loop code that determines when a draw cycle should happen, and it is the screen layer dirty flags that indicate which layers need to be redrawn. This code now handles patching I/O out, just before calling a screen layer's draw routine, and patches I/O back in automatically when it's done. Your app will hardly notice.
Interrupts are disable during the draw cycle, so that nothing interrupts the draws. And further, your app's draw routine should be geared to happen as quickly as possible. It would be completely insane if you were trying read something off disk during a draw cycle. So not having access to I/O during a draw routine should not be an issue.
Now, what about color memory? To draw to color ram, you have to have I/O mapped in. Here's how I've got it set up in C64 OS. Your app just draws to $D800-$DBFF like normal, as if it were blitting to color memory. However, it will in fact be blitting into a main ram color buffer. All the screen layers that need to will have an opportunity to draw, in the correct order, and they will each blit their colors over top of the previous layer's colors. When all the draw routines are done, the main event loop code handles copying the underlying color buffer into the I/O mapped color ram. I've tested it, and when done in parallel, it happens really damn fast.
In fact, there is actually a bit of an advantage to doing it this way. When the draw routines were each writing directly into color memory, the VIC-II would sometimes have a chance to refresh part of the screen mid-draw, while an underlying layer set, say, the menu bar background color to blue, before the menu layer had an opportunity to set it back to say cyan. This would sometimes result in a slight flicker. By buffering all the color changes, and copying them to color ram at the end in one step, this flicker is eliminated. In fact, often, it results in the color values in real color memory never even changing.
Lastly, what to do about the paged memory map that we used to have stashed under I/O? Well, that's an easy one. Recall, it only used up around 150 bytes under I/O, but Screen Memory used to take up 1000 bytes down in workspace memory. Now that screen memory is tucked away behind I/O, we can move our paged memory map to $0400-$04FF. But, nothing is using the other 3 pages, $05xx, $6xx and $07xx, so we can enable those as available in the paged memory map, and start loading app's to $0500 instead of $0800.
And lo and behold, under this new configuration, with a custom character set in ram and ready to be customized with beautiful UI elements, and much needed ASCII symbols, we have actually MORE memory for our application to work with!
I call that a win.
In an upcoming post, I'll get into the weeds a bit on how I intend to make use of the custom character set, now that C64 OS has one available to it.
As always, please leave any comments, feedback, corrections and questions in the forums below.
- Interesting side note. The pixels on a real C64 are not square. They're taller than they are wide. Thus, on a real C64, 320 pixels wide has the same physical width as just 200 pixels is tall. A C64's 320x200 looks square. But on a Mac, which has square pixels, a C64's display looks letter box shaped. [↩]
- Read my earlier post, How the C64 Keyboard Works, for an in depth discussion of what a "port" actually is. [↩]
- These memory configuration changes are instantaneous, by the way. You can write a value to $0001 to patch in I/O, and on the very next cycle read from I/O. You can write to $0001 again to patch I/O out and without any delay, the very next cycle, write to the underlying ram. [↩]
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