Sunday, February 1, 2015

Final Thoughts for RC2015/01

The end of January has past, and thus came the end of another RetroChallenge event. As usual there were a number of interesting projects started, several completed, and a few derailed along the way. Count my port of Fahrfall to the Apple II in the completed category! Of course, no software is ever finished... :-)

Alpha Release

I want to make a binary available for my Apple II version of Fahrfall, but my "real world" knowledge of using an Apple II is still lacking in several ways. One of the things I don't know how to do is making disk images! In fact, I'm not even sure what sort of binary format is needed for an Apple II machine language program to be loaded from diskette...

While developing Fahrfall, I have been using the built-in monitor program on the Apple II in conjunction with a Super Serial Card and the "IN #2" and "PR #2" commands in BASIC. I used a 6502 assembler running on my Linux laptop to generate the machine code for Fahrfall, and I used a script to convert the binary output from the assembler into a series of monitor commands that I could send over the serial port. I am now making this file available as "fahrfall.txt" in the directory below:

http://www.tuxdriver.com/download/fahrfall/apple2/alpha-1/

In order to load the program, start the monitor (CALL -151 at the BASIC prompt) and upload the file through the serial port (assuming that you have already done the "IN #2" and "PR #2" as described above). Once the upload is complete, start the program executing at location $0C00 (C00G at the monitor prompt). In order to play with a joystick, just press the joystick button to start the game and use the joystick to move left and right. If you want to play with a keyboard, just press the spacebar to start the game and use the "open apple" and "closed apple" keys for left and right.

A Tale Of Two Platforms

At this point, I am tempted to make some comparisons between the CoCo and the Apple II and/or between the 6809 and the 6502. Instead, I'm going to avoid feeding the trolls and just say that both machines have (sometimes hidden) strengths and (often obvious) weaknesses that probably influenced the types of software that were historically available on each machine. There are differences between the CoCo and Apple II versions of Fahrfall, many of which are due to the strengths and weaknesses of the respective platforms. I hope that both versions feel like "native" games to the users of those platforms, as long as that doesn't interfere with making Fahrfall fun!

Moving Forward

As for what is next, that is hard to say. I like the colorful, moving background in the CoCo version of Fahrfall and I would love to bring something like that to the Apple II version, if possible. Some amount of music might be a nice addition to the Apple II version as well. Plus, there might be a better way to handle keyboard input? I'm not sure...

For some time I have wanted to go to KansasFest, a yearly Apple II convention. By all reports it is a great event, but my limited Apple II experience made me feel like too much of an outsider to be sure of having a good time. Now that I have an Apple II version of Fahrfall, at least I would have something to discuss with other KansasFest attendees! So, this may be the year I make the trip.

Anyway, I guess that is all I've got for now. I would like to thank the organizers of the RetroChallenge event as well as all of the other participants. As usual, the past month proved to be a great learning experience, a source of great reading material, and an excuse to have some good retro-fun. I hope these events keep coming until the end of time, and I intend to be involved whenever possible. I suspect that you, Dear Reader, feel the same way!  So as alway, I expect you to stay tuned... :-)

Saturday, January 31, 2015

Feature Sweep

The end of RetroChallenge 2015/01 is nigh! But is Fahrfall running on the Apple II? Well, sure it is (at least for the IIe and above)! The last couple of days have seen a flurry of feature development, but I think the result is looking fairly good...


Urge To Merge

My last post talked about adding color patterns to the platforms and using another LFSR to change the platform colors. While writing that code, I discovered that I was close to the edge of a performance barrier for drawing during the vertical blanking interval. I thought that was resolved by some code optimization, but the issue returned when I attempted to merge the changes into the main game code. Fortunately I was able to find a few more minor tweaks that allowed me to merge the color changes and to still draw everything within the allotted time constraints.


Making Introductions

Games generally have a starting and an ending, and it is appropriate to mark those points prominently. Like any game, Fahrfall needs to introduce itself to the player and it needs to tell the player how well they performed. The Apple II offers a video mode that allows for graphics on most of the screen while providing four lines of text at the bottom of the screen. Fahrfall now uses this mode to display some colorful graphics while communicating to the player through text at the bottom of the screen both before and after a game is played.


Keeping Score

With the "intro" and "game over" screens in place, it was finally time to add scoring. The score itself increments as a timer while the game is in progress. Since the score is displayed as a decimal number, this seems like a perfect application for the BCD support built into the 6502! This allows for maintaining the score just like any other binary number, while making conversion of the score into screen data almost painless.

I have been using a joystick while developing the Fahrfall code, and using a joystick is the preferred method of play. Not everyone will have a joystick, so supporting the keyboard is desirable as an alternative. Unfortunately, the keyboard repeat rate is too slow to support use of the arrow keys for controlling Fahrve. (Maybe I just don't know how to get around relying on the keyboard to report repeated keys?) However, unlike most of the keys on the Apple IIe keyboard the "Open Apple" and "Closed Apple" keys are accessed directly through "soft switches". This lets me poll those keys directly at any time, so I am using those keys for "left" and "right" when keyboard access is selected. (FWIW, the keyboard access selection happens when the <spacebar> key is used to start the game.)

The game ending "sizzle" is like a Fahrfall trademark. Some people hate it, but it always gives me a chuckle to watch a new player nearly jump out of their skin the first time they end a game! The sound itself is just a short burst of noise, implemented by using the LFSR to determine when the speaker is toggled. This addition is a nice "final touch" to make Fahrfall seem complete.

Well, I think that is "pencils down" for my RetroChallenge 2015/01 entry. I now know a lot more about programming the Apple II and the 6502 processor, and I have an Apple II port of Fahrfall to show for my efforts. I reckon this counts as a success! I'll probably be back later today or tomorrow with a wrap-up post, so I hope you will stay tuned... :-)

Monday, January 26, 2015

Over The Wall

We are now in the final lap of the Retrochallenge 2015/01 event! Time is short, and Fahrfall still lacks some key features (like keeping score)... Worse, I got a bit distracted while chasing a performance issue!!

Candy Canes Aloft

In Fahrfall, as the game progresses the color of new platforms changes periodically. I enjoy this feature, as it is both an indicator of how far the game has progressed (almost like a score...), and it rewards the player with something new in return for playing a bit longer. The graphics in the Apple II version won't allow for the "checker board" patterns available on the CoCo, but it will allow for both solid colors and a striped pattern that looks a bit like a "candy cane".

The existing code for drawing platforms only worked with single-colored platforms. I made the necessary modifications to allow for multi-colored platforms, and added some scaffolding to feed random color patterns to Fahrfall. This resulted in multi-colored platforms, but it also resulted in Fahrve's head disappearing before he reached the top of the screen!


Crossing The Line

For a while, I struggled to find the cause of this issue. I carefully checked the code changes, and I even went so far as to rewrite and test the changes in a step-by-step manner. I was almost at a loss as to the cause of this problem, until I noticed that Fahrve's head seemed to make it slightly higher up on the far right of the screen than on the far left. That observation led me to believe that I was overrunning the end of the vertical blank interval before finishing with drawing Fahrve.

With that suspicion in hand, I applied a trick for checking the timing of my vertical blank drawing work. When I start the vertical blanking period, I switch the video mode to showing text instead of low-resolution graphics. (These modes share the same video buffer on the Apple II, and the background data for the graphics screen displays as visible characters in text mode as well.) I switch the video mode back to graphics after Fahrve is drawn, which would be indetectable if done before the end of the vertical blanking interval. But as suspected, it was clear that my drawing code was not completing in time.

Redemption

Up until now, the code in the Apple II port of Fahrfall has largely grown organically. Heeding the axiom that premature optimization is the root of all evil, I have largely focused on getting things working rather than making them run as fast as possible. Unfortunately, at some point things that run too slowly simply aren't working, whether they are correct or not. Drawing during the vertical blanking interval is, in fact, a "real time" programming task! :-)

In my game loop, I was erasing the old platforms and the old Fahrve, calculating movement and detecting collisions, then drawing the new platforms and the new Fahrve. As an optimization, I delayed the movement calculations and collision detection until after drawing the new objects. This bought a little time, but not enough to meet the required deadline.

Something else I was doing sloppily was erasing the platforms. For this I was actually using the same routine I use to draw the platforms but with the data changed to use the background color. The new multi-color platform drawing code takes longer to do its job, and using that routine for erasing the platforms made that take longer as well. I replaced the platform erasing code with a simpler loop, and this was enough to meet the drawing deadline.


So, there you have it -- multi-colored platforms are possible on the Apple II port of Fahrfall. There isn't much time left during the vertical blanking interval for any other drawing tasks, but right now what is needed are less time-sensitive measures -- like scoring! I don't think I'll be able to reach a totally finished game by the end of the month, but I hope to get a bit closer than I am now. If you are curious to see where I end-up, then you will just have to stay tuned!

Sunday, January 25, 2015

Fahrve Lives!

We are still barreling towards the end of Retrochallenge 2015/01, and the Apple II port of Fahrfall is still progressing nicely. We must be getting close, as the star of our show has finally started showing-up for dress rehearsals. Yes, ladies and gentleman, Fahrve now joins our party!

Low-Res Perspective

Representing Fahrve on the Apple II is a little tricky, at least when using the low-resolution video mode. The CoCo version of Fahrfall uses a video mode with a resolution of 64x96, while the Apple II low-resolution mode is 40x48. Obviously some scaling is required, but at such low resolutions scaling inevitably leads to distortions. Fahrve was already only slightly better than a "stick figure", and on the Apple II that is more true than ever. In particular, cutting the vertical resolution in half forced some compromises in the shoulder details and the thickness of the feet. The extra horizontal thickness of the blocks forced spreading the Fahrve graphics a bit wider as well, in order to get separation for the arms and legs.

A Tale Of Two Fahrves

Another quirk of the Apple II low-resolution mode required some consideration. The color of each pixel is encoded with 4-bits of data and two pixels are encoded in each byte, but the two pixels encoded are on different lines. This means that two pixels encoded in the same byte when plotting a figure on an even numbered line are suddenly encoded in different bytes when plotting the same figure on an odd numbered line!

This problem is not really any different from the problem of plotting a figure in a mode where multiple horizontal pixels are encoded in the same byte. One option is to use shift and rotate operations to transform a source image into a properly adjusted set of values in the video buffer. But that method is slow, and already awkward when dealing with horizontally encoded pixels. Using bit manipulations on vertically encoded pixels seems potentially even more awkward.

A better performing option is to use a different version of each sprite for each data alignment option. This increases the amount of memory required for sprite handling, but in this case we only require two versions of each sprite, one version for even-numbered lines and a second version for odd-numbered ones. Since I already have to keep track of Fahrve's vertical position anyway, choosing which sprite to display is reduced to a simple odd/even check on that number.

Sidestep: The Issue

When Fahrve moves from side to side, I depict him with one foot up and the other arm up as if he is shuffling to the side. The foot/arm up combination alternates between left/right and right/left as Fahrve continues to move on the platforms. Obviously, each of these sprites needs odd- and even-numbered line versions as well. That is four versions of Fahrve for horiztontal movment alone!

Along with the sideways-moving versions of Fahrve, there are odd and even versions of Fahrve standing still on the platform. There are also odd and even versions of Fahrve falling through the air with his feet pulled up and his hands above his head. Altogether there are eight versions of Fahrve for Fahrfall. For maximum performance, these are implemented directly in code rather than as bitmaps in data. In truth, this extra code nearly doubled the size of Fahrfall. Of course, that took it from slightly more than 1K of code to just under 2K... :-)


As you can see, the game is really coming together. I have to admit that despite the lack of scoring, I can now occupy a few enjoyable moments of my time playing with Fahrfall on the Apple II. Still, it doesn't really look a lot like a game just yet. If you want to see how that turns-out then you'll still have to stay tuned!

Friday, January 23, 2015

Make Some Noise

There is a bit more than a week to go in the RetroChallenge 2015/01 event, with just this weekend and the next remaining.  It is getting to be crunch time! :-)  Fahrfall is far from complete, but it is shaping-up fairly well...

Where Am I?

Fahrfall does screen updates during the vertical blanking interval. This provides for smooth, well timed, and glitch-free animation. Only the Apple IIe and later Apple II models provide hardware support for this sort of timing, but I don't think that is a major limitation. A bigger problem is that while the IIe, IIc, and IIgs all provide support for synchronizing to the vertical blanking interval, they each do so in a slightly different (and incompatible) manner. In order to run on all of these Apple II machines, Fahrfall needs to be able to figure out which kind of machine is running it and to adapt accordingly.

Fortunately, it is not too difficult to identify the host machine. In fact, a pertinent Apple Technical Note ("Apple II Family Identification") is readily available on the Internet. Executing a simple ROM routine quickly identifies whether or not a IIgs is in use. If not a IIgs, then a couple of additional memory accesses to known ROM locations quickly distinguish between the IIc, the IIe, and older machines as well. All that remains is to make use of this information.

A single bit within the IIe indicates whether or not vertical blanking is active, so the IIe code simply polls that bit. The bit value for active vertical blanking is reversed on the IIgs, so a complimentary routine is provided for use on the IIgs. On the IIc the vertical blanking indication is tied to an interrupt source, so the IIc routine instead checks for an active interrupt and clears the interrupt source before returning. An indirect jump through a pointer in memory is used to choose the appropriate vertical blank synchronization routine at runtime. This should be sufficient to enable Fahrfall to support all of the later members of the Apple II family.

The older II and II Plus are also detected during Fahrfall initialization. Lacking any support for vertical blank synchronization, detection of these early Apple II machines currently leads to an early exit from Fahrfall. When Fahrfall is more complete, I may experiment with a more static delay loop for use on these early machines.  It won't be perfect, but it might be better than not running at all? Alternatively, it looks like the mouse interface card might be able to provide the necessary timing (if one is installed). Are there enough II or II Plus machines (with or without mouse cards) to make the effort to support those machines worthwhile? I have no idea...

Read The Fine Manual

Most programming of Apple II hardware is done through some sort of "soft switch" access. These are memory-mapped hardware registers that change and/or reflect the state of various bits of hardware on the Apple II. Most "soft switches" are accessed in such a way that the change of state occurs based on the location accessed rather than whether that access is a read or a write. However, "most" and "all" are different things...

The vertical blanking interval detection routine for the Apple IIc enables generation of a vertical blanking interrupt, but disables interrupts at the CPU. The interrupt is detected by polling a "soft switch" until the vertical blanking interval is indicated. During setup, another set of "soft switch" locations is accessed to enable the generation of that interrupt. But when I first ran Fahrfall on a IIc, the program simply hung.

I speculated that my polling loop was checking for the wrong condition, so I changed it to look for a '0' rather than a '1'. That stopped Fahrfall from hanging, but now the game ran too fast as it always thought an interrupt was pending. Ultimately, I figured out that my attempt to enable vertical blanking interrupt generation with read operations was not working. Changing those to write operations made the interrupt detection work as expected. Sometimes it pays to read the manual carefully!

Hear Me Roar

One of my favorite bits of Fahrfall on the CoCo is the simple sound effect used to simulate footfalls during player movement on the platforms. This effect uses the square wave audio output on the CoCo, following a pattern triggered during the vertical blanking interval. The Apple II audio hardware is similar to the square wave audio hardware on the CoCo, so I implemented an equivalent audio pattern triggered during the vertical blanking period while the player is moving horizontally on a platform. The sound effect on the Apple II sounds virtually identical to the effect on the CoCo version of Fahrfall. Fahrve still looks like a big, blue box, but now you can hear him coming!


I guess that covers the state of things for now. Hopefully I can find some time this weekend to add scoring, or to give Fahrve some shape, or... Well, I guess you'll just have to stay tuned!

Tuesday, January 20, 2015

Game On

A few more days have passed in the RetroChallenge 2015/01 event, and there is a bit more progress to report!  Step by step, Fahrfall is coming together on my Apple II.  Soon I will have a real game... :-)


Collision Detection

The main point of Fahrfall is to fall down the screen, land on a platform, and ride the platform back up the screen before you repeat the process.  For that to happen, the program needs to know when the player is in contact with a platform.  That problem is known as collision detection.

Collision detection in Fahrfall is fairly simple.  Each platform is represented in the computer's memory by a bitmap, with a '1' representing each section of the platform and a '0' representing each gap.  A variable keeps track of the horizontal position of the player on the screen, and that value is used as an index into a table containing bitmaps that represent the platform sections with which the player might collide in that horizontal position.  When the player's vertical position is immediately above a platform, a simple bitwise AND operation between the two bitmaps is done.  A non-zero result represents a collision (i.e. the player is standing on a platform).

Hidden Platform

As indicated, the collision check is done between the player and the platform on the line beneath the player.  This presents a problem when the player is all the way at the bottom of the screen, since all three platforms will be above it.  Without some accommodation, the platform data array will underflow and problems will be inevitable.

There are many potential solutions.  There could be an explicit check for this situation, with the result being either to skip the collision check or to immediately end the game.  Alternatively, a fourth entry could be added to the platform data array.  The fourth entry could contain an all-zero bitmap (so that no collision would ever occur), or it could contain the data for the next platform.

The latter option is what I chose to do.  This may seem silly, since only three platforms are displayed on the screen.  But adding a fourth (off-screen) platform introduces an interesting gameplay feature.  If the next platform would catch them, a player can be saved at the last minute through sheer luck when falling off the bottom of the screen!  This only happens if the fourth platform is just about to emerge, but it still adds an element of fun for those who reallly enjoy Fahrfall.


Moving On Up

With collision detection all sorted, making the player ride the platforms is a simple matter.  When a collision is detected and the platforms move up, the player is moved up at the same time.  Similarly, if a collision is detected then the player does not move down.  To make this work properly, collision detection must be run whenever there is movement of either the player or the plaforms (i.e. sometimes twice in a frame).  Aside from that, making the player ride the platforms was just a simple matter of programming.

So the basic gameplay mechanics of Fahrfall are complete in this Apple II port of the game.  At this point you can "play" using the joystick, so long as you don't care about keeping score.  Nevertheless, there are still plenty of features and embellishments missing.  So there is still more to come...I hope you will stay tuned!

Sunday, January 18, 2015

Halftime for RC2015/01

We are now roughly half way through the Retrochallenge 2015/01 event!  I am still making progress, but I don't have a very cohesive topic for a blog post at the moment.  Since it has been a few days since the last update, I will just touch on a few random bits here and there...

Line By Line

In an earlier post, I mentioned that the video memory in the Apple II has a peculiar layout.  For example, the data for line six is offset in memory from the data for line seven by a different amount than the offset between the data for lines seven and eight.  This makes moving an object on the screen from line to line more complicated than simply adding or subtracting a fixed offset from the previous address.

Previously I wrote a routine that would convert a screen line number to a base address in video memory.  I have since rewritten that routine by replacing a shift-and-add multiplication with some bitwise operations, thereby improving its performance.  Nevertheless, the routine is long enough that I would like to limit how much it is used.

I was able to implement somewhat simpler routines for moving up or down one line at a time.  These routines combine simple offset adjustments with checks and adjustments for crossing 8-line boundaries.  The results are somewhat simpler than the direct calculation routine, with corresponding performance improvements.  A single-line offset is simple enough to be practical and common enough to be useful, especially for Fahrfall.


Point Break

The 6502 only has a handful of registers for use by the programmer, none of which are large enough to hold an actual address.  Addresses for data access can be embedded in the program code, but either that would limit a given function to accessing only predetermined memory locations or it would require the use of self-modifying code that rewrites the addresses whenever different memory locations are used -- ugh!

Fortunately, the 6502 does provide a couple of means for storing an address in memory and then using that address as the object of an indirect memory access.  Unfortunately, those options require storing the address somewhere within the 256-byte "zero page".  Those locations are in high demand, and the Apple II firmware pre-allocates most of that memory for its own use.  Fortunately I was able to find enough space to fill my immediate need for a drawing pointer.

When I only had platforms on the screen, I could get by with a single drawing pointer.  Even though there are three rows of platforms, their relative positions makes it easy to adjust the drawing pointer to point to each platform in turn.  Unfortunately, the player object needs its own drawing pointer value which is independent of the platform drawing pointer.  Of course, the player drawing pointer needs the same sorts of manipulations as the platform drawing pointer needs.

One option would be to use separate "zero page" locations for each pointer and to duplicate the pointer manipulation routines for the player object drawing pointer.  A better alternative is to use a single "zero page" location for the active drawing pointer and to save/restore this value to/from separate memory locations for different uses.  The second option effectively treats the "zero page" drawing pointer as a 16-bit register, providing me with the flexibility I need for what I want to do.


Ode To Joy

Fahrfall is an arcade-style game, so it makes sense to use a joystick to play the game.  The joysticks on the Apple II use an analog circuit based on an RC time delay.  The actual timing is done at the CPU by counting the number of cycles that are spent executing a tight loop while a capacitor is charged.  Coding such a loop seems simple enough, but doing so is unnecessary -- the Apple II firmware already contains such a function at a well-known location.

Reviewing the actual code of the firmware's joystick reading function reveals that there is little opportunity for improving its performance.  Consequently, I used the Apple II firmware to read the Y-axis of the joystick.  This returns an 8-bit number which represents the position of the joystick on that axis.  Fahrfall only needs to know the general direction for movement, not the actual position.  So, I added code to take the number representing the joystick position and to test it for left and right ranges.  I also included a dead zone in the middle to represent "no movement".

Well, that covers a few micro-topics related to my port of Fahrfall to the Apple II.  I also finished reading Assembly Lines: The Complete Book, so I guess I am now a trained Apple II programmer...

Things are coming together, but there is still more to come.  I hope you will stay tuned!