Sunday, 18 June 2017

Asteroids on the Apple II - coming soon to a screen near you!

I was woken at 4:30am this morning with the knees of my 2 yr old son wedged against my back. As you do at my age and you're woken through the night, I stumbled to the bathroom and, still half asleep, took a seat.

Why am I telling you this? Because this blog is all about the process, as well as the technical details, and to this day I still marvel at the circumstances under which my brain still manages to have epiphanies. I'm not sure I even consciously realised I was thinking about Asteroids, but at that moment it came to me that the Digital Vector Generator (DVG) ROM was (also) mapped into the 6502 address space, and that the 6502 code was reading it whilst generating the display list. And of course that ROM was conspicuously absent from the Apple II binary image.

Well tonight I rectified that situation and, after battling IDAPro for a while getting a second binary file loaded into the correct segment at the correct address, was soon able to generate a now-12KB binary that included both the DVG ROM image and (patched) 6502 ROM image.

[As an aside, it only occurred to me during all this that the Apple II .BIN file format is woefully crude, lacking not only the ability to load a single file into a non-contiguous address spaces, but also lacking an explicit execution address.]

Anyway, first order of business was again comparing the display list of the first frame with that generated on arcade hardware. Gone were the large groups of zero bytes; it looked roughly the same size now, and a lot of the data was the same, but it still differed.

Before going further I needed to confirm that the contents of the display list are completely deterministic. Asteroids explicitly zeroes working RAM, so that wasn't the issue. It also makes many calls to a pseudo RNG routine - it's a 16-bit single-tap (IIRC maximal-length) LFSR for those interested - but thankfully none from the NMI, which isn't running (yet) on the Apple version. I couldn't see any other reason to suggest it wouldn't be deterministic. And to be sure, I ran the arcade emulation twice, and the 600th frame on each occasion was identical.

Since the first byte differed, I set a breakpoint in the MAME debugger where it was written to the shared (display list) memory. Not surprisingly, it was a low-level routine that revealed nothing of the origin. Here's where the trace command in MAME comes to the fore; I was able to manually trace back through the code, and see where either the data, or the execution path, differed between the two platforms.

In this case it happened to be the value read back from a coinage dipswitch (or rather shadow zero page value to be precise) that differed. The Apple II version was, not surprisingly, reading back as zero which was freeplay!

I simply fired up the arcade emulation, changed the dipswitch to read back as zero, and compared the first frame of each again. Identical! Then I compared the 600th frame from the arcade version with the Apple version. Eureka!

So now I have the arcade Asteroids 6502 code executing on the Apple II, producing identical output!

To be honest, the whole process has actually been a little less painful than I had expected. All that is required to get this far is patching 4 bytes in the 6502 ROM. I guess like the old joke about the X on the pipe; it's not the value of the 4 bytes that's the hard bit, it's knowing which 4 bytes to patch!

So what's required now to get a playable game?

My next step is (probably) going to be building the code to render the display list to the Apple II video every frame. At this point it'll be a simple matter of calling the routine once from the main loop once it has rendered the display list, immediately before it returns back to the start of the loop. At least I'll get to see the attract mode running.

I should note that the game on the Apple is currently not throttled in any way at all - it simply generates frames as fast as the 6502 code runs before looping back for the next frame. On the arcade hardware, the NMI provided a periodic 'interrupt' to drive the timing of the main loop (now patched out). So at some stage I'll have to add that back into the Apple build.

The NMI also had the task of reading all the hardware, debouncing controls, and formatting it all into shadow variables in 6502 RAM. This is where the Apple II code will differ quite a bit, reading keyboard, joysticks and possibly menu settings.

I'll touch on the sound at a later date.

It's quite neat that the core 6502 code will be running pretty much untouched. I can see now why Norbert simply loads the original arcade ROM images into his emulators and (likely) patches a handful of bytes. The Apple II-specific code will be confined to the NMI and display hook routine.

Of course it also allows alternate display hook routines; different video modes and/or even different platforms. Interesting possibilities...

No comments:

Post a Comment