I made a good start on my C64 retro CRPG project over the weekend, with a “map” of sorts being displayed and scrolled around. But then I tried to add some disk I/O to load and save segments of the map, and there I ran into a wall.
It’s a little bit my own fault for trying to be clever. I was thinking about my 256 byte map segments and the 1541 disk drive’s 256 byte blocks, and it seemed like such a perfect match. Instead of bothering with a directory structure and hopping around to track 18 in order to get anywhere, why not directly read and write to the blocks? I could keep a little index in RAM and then directly seek to wherever I wanted to go.
That’s when my emulation environment started getting in the way. I’m doing cross-development with cc65 as my compiler, and when I started I wrote a build script that compiles my code and then launches it in x64, the VICE C64 emulator. As long as I was just running a single file, that Just Worked. It started up instantly and ran my program. But once I needed a disk image, things got confusing.
VICE includes a command-line tool called c1541 for creating and editing disk images. Getting x64 to load a single disk image and autoboot the first file worked, but I was stuck at real 1541 speeds for loading the program. What I really wanted to do was load the disk image on one drive and still autostart the program as a standalone file, and I never could get that working.
Eventually I ended up with two disk images – a program disk on drive 8 and a data disk on drive 9, but even that caused some weird behavior. I found that x64 didn’t quite handle the second disk correctly, and in order to send commands to it I had to send it a manual initialization command first. That’s not a terrible problem, but I wasted a lot of time figuring it out.
My overall impression is that the autostart and disk attachment command line options in VICE are kind of brittle and not intuitive. Of course, there might be perfectly good reasons for the behavior that I just don’t understand. I have downloaded a copy of the VICE source code, but for now I’m avoiding the temptation to get sidetracked into another project.
Anyway, if I did want to muck around with those kinds of details, I have my own d64/g64 library to play with. Maybe some other time.
Part of why I spent so long messing around with how to launch things in VICE is that I was looking at the disk structure in c1541 and confusing myself with what I saw. After doing more research, it looks like my confusion was actually being caused by a bug in the 30+ year old DOS that’s built into the 1541 disk drive.
The code I was trying to write bypassed the 1541’s directory structure and wrote directly to specific blocks on disk. But I still had to make sure the DOS wouldn’t overwrite that data when saving some other file. The 1541 uses a BAM (Block Availability Map) on track 18 to record which blocks are and aren’t being used. There’s a command the C64 can send to the drive to mark a particular block as being in use.
Unfortunately, this block-allocate command is really broken. What seems to happen is that changes aren’t flushed back to disk correctly, and when you try to allocate a block the disk drive will instead allocate the entire track (each track contains between 17 and 21 blocks, depending on how close to the edge of the disk it is).
I found a post on Lemon64 that indicated a possible fix. If you open a second channel to the disk, send the allocate command, and then close the second channel, it will update the BAM correctly – as long as the block you’re asking for is free. If it’s already in use, forget about it. The data in the disk drive’s RAM is correct, but the BAM on disk will still get whole tracks of data incorrectly marked as being allocated.
Frustratingly, an explanation of this behavior was right under my nose the whole time. I’ve been using the 1541-II User’s Guide for reference, but Commodore was never any good at updating their documentation, and that booklet glibly describes block-allocate as if it’s a thing that actually works. However, the book Inside Commodore DOS by Richard Immers and Gerald Neufeld has a detailed explanation of this problem – along with several other critical bugs in the 1541 DOS.
The irony is that I actually picked up a copy of Inside Commodore DOS at VCF Midwest just a few weeks ago! It didn’t occur to me to look at it until after I’d pretty much worked things out for myself, but it is good to know that my conclusions were correct.
So it’s been a slow couple of evenings piecing together what’s going on. And now I still need to work out an efficient but correct way to handle the disk I/O for my map segments.