There comes a point in ever retrocomputing project where you realize you’re not giving enough credit to the people who did this stuff back when it wasn’t retro. There’s a tendency to think that with modern development environments, modern access to information, and a modern computer science background, these problems will just topple like so many dominoes, but the reality is that even today a project like this is hard work.
I am having fund, mind you – enough that I’d rather be working on the code than blogging about it. But I’m about at the point where I need to call my work on the mapping part of this engine “good enough” so that I can move onto another big chunk of code – the entity system.
In our last exciting episode, I expressed my frustration that bugs in the 1541 DOS were interfering with my plan to bypass the file system in order to efficiently store 256-byte chunks of map data on disk. Since then, I’ve basically decided “to heck with it!” I’m going to have a data disk full of map data, entity data, and whatever else I end up needing, and I’m going to completely ignore the disk drive’s screwed up block allocation routines. This basically means that if anyone (meaning me) saves a regular file to the data disk or, heaven forbid, validates it, all that data goes bye-bye. It’s like the dumbest form of copy-protection ever, and I therefore officially declare this problem a feature, not a bug.
I still have the problem that the only tool I have for editing that data on disk is the game engine itself, and so I have endowed it with a completely inadequate, but essentially functional, set of map-making tools. I can load a map, wander around it, and edit the tiles on the map – one square at a time. Then I can save it to disk, and the best part is that it’s completely secret. There’s no hint in the disk directory that there’s anything even there. It’s kind of ridiculous.
I’ve also – and this took a lot of mucking around with the math of the coordinate systems – gotten dynamic map loading working. Right now I keep up to 9 16×16 segments of the map in memory, but the maximum size of a map is much larger. Now, when the on-screen map window moves over part of the map that hasn’t been loaded yet, that segment is paged in from disk, replacing a segment that’s currently off-screen. It’s not completely seamless, since this is a single-CPU computer and I don’t even have a fastloader yet, but it does work.
How big can maps be? The indexes I’m using can support up to 16 segments in either direction, for a maximum map size of 256×256 squares – equal to the world map in Ultima IV or V. Mind you, I’m not actually going to be making a map that size. At least not until I have a more convenient set of editing tools.
One feature those games have that I’m not supporting right now is wraparound when you move off one edge of the map and appear on the opposite side. I spent several hours looking at it, and supporting wraparound really does add a lot of complexity. And not just to the renderer, but to player input, entity AI, and a bunch of other systems. In the end, I decided it didn’t really fit the sort of game I’m thinking about writing. Besides, there’s nothing particularly realistic about a toroidal world map.
Last night, I worked out the basic support for one more important part of the map renderer: custom character sets. When I started, I just used some of the C64’s built-in graphics characters to give an impression of water, plains, and mountains (the only tile types I’ve put in so far). In a real game you want customized tile graphics.
The default character set in the C64 is hidden away in a chunk of ROM that, by default, is only visible to the VIC-II graphics chip. That same region of memory is also home to the C64’s IO devices (mapped in by default) and 4k of RAM. Which of those three things the CPU sees at that address range is controlled by bank switching, using the special IO port build into the 6510 CPU.
You can tell the VIC to look somewhere else for the character data, within certain limitations. First, the location has to be aligned on a 2-kB boundary. Second, the VIC is only capable of “seeing” one 16 kB bank of the C64’s memory at any one time. Instead of using the 6510’s IO ports, the bank for the VIC chip is controlled by an output port of one of the two 6526 CIA chips. Whew!
One implication of this is that the character data and the screen memory have to be in the same bank, and because I didn’t think of this beforehand I had to shuffle around my data structures quite a bit in order to accommodate the new character data. Luckily, the cc65 compiler and its linker give me quite a bit of flexibility about how my program will be arranged in memory, and I’ve also configured the linker to reserve a large section of memory for fixed-location custom data structures, which now includes the map data and character data.
The final task was to come up with the actual new character data, and as you can see from the graphic up top… let’s just say that’s going to be an ongoing process. The white and gray bits are supposed to be mountains, if that helps any.