The month is drawing to a close, and this will probably be my last post before the end of the Retrochallenge 2013 Winter Warmup event. So, let me give a quick update on the latest developments. I'll also tell you where to get my Simon clone, in case you want to try it for yourself...
Draggin' The Line
I recognize that a software project which requires hardware that only I have is a bit like masturbation. Don't worry! I took pity on you and implemented keyboard support in my Simon clone. I hacked a keyboard handling routine onto the end of my game loop. Within that code I map the W, E, S, and D keys to the four colors of the game. Any key can be used to start the game.
In case you are wondering, that key map was not my first choice. I'm right-handed, and I originally thought to use the U, I, J, and K keys. The CoCo uses a typical matrix circuit for it's keyboard, and it turns-out that the U, I, and J keys are all on the same row of the matrix. That would be fine, but it happens that the CoCo also uses two of the keyboard matrix row inputs for reading the joystick buttons. Of course, the line for the left joystick button is the same as the line for the U, I, and J keys. Pressing the joystick button tended to be interpreted as pushing one of the keys on the keyboard! Not good.
I suppose I could have just switched to the right joystick port, but the left joystick port seems to be the traditional favorite for player one (or a single player). I also tried to avoid the problem by checking the joystick button immediately before checking the keyboard, and skipping the keyboard check if the joystick button was pressed. That helped a little, but fate still found a way for the joystick button to interfere with the keyboard. Fortunately, it wasn't too hard to find another grouping of keys that did not interfere with the joystick.
Enter The Dragon
The Dragon is extremely similar to the CoCo -- so similar that most software written for one will work on the other with few if any changes at all. One sticking point is that the keyboard matrices are different. The difference is minor, amounting to a mere shifting of rows of keys. But, it is certainly enough to scramble the mapping between keys and their functions.
A variety of solutions to this situation are possible. These include avoiding the keyboard completely, producing slightly different binaries for both the CoCo and the Dragon, or doing some sort of runtime probe to make a guess about which machine is running the software. The approach I took is to simply enable both sets of keyboard inputs. The W, E, S, and D keys should work on both the Dragon and the CoCo, while each will have four other keys that will also operate the game. It will be a little weird if you happen to hit one of the other active keys, but I don't think that will be a big hindrance to enjoying the game. Oh, and those keys don't interfere with the joystick buttons on the Dragon either!
Ship It!
Software is like art: it is never finished, just abandoned. Simon is reasonably well defined, but I'm sure that I could continue to tinker with this implementation indefinitely. One of the great things about the Retrochallenge events is that they have a deadline. In honor of that, I am releasing Follow Me, my clone of Simon for the Tandy Color Computer.
The git tree is available now. In addition, I have made some files available for download:
Follow Me LOADM'able binary: followme.bin
Follow Me CLOADM'able audio file: followme.wav
License information for the above: COPYING
Text file describing the above: README
New Horizons
So, what next? Well, there is The CoCo Coding Contest, which is already in progress. I plan to expand upon my work from last summer's Retrochallenge event, using my 44-color CoCo graphics mode to make a sliding puzzle game. Winners will be announced at this year's Chicago-area CoCoFEST! I hope everyone can make it there this year! :-)
I recently realized that the Tandy 1000 used the same joysticks as the CoCo. Also, the Tandy 1000 has some interesting sound and video hardware that is a bit different from other PCs from that era. Maybe it would be fun to implement something to use my rotary controller on a Tandy 1000? Well, I guess we'll see...
Wednesday, January 30, 2013
Sunday, January 27, 2013
Game On!
The end of the Retrochallenge event draws ever nigh. Fortunately, it looks like I will not be empty handed when the competition closes!
Feature Complete
I have now implemented all of the basic features of Simon. I can generate random tone sequences, play them for the user, monitor the user's control inputs, and evaluate those inputs against the generated sequence. I have added a timeout when monitoring the inputs. A slow or inactive user loses the game, as does one that fails to match the tone sequence. Losing the game results in a restart, while matching a maximum length sequence (31 tones) earns the player a congratulatory tone and a "you win" screen. FWIW, I also think that the game is fun to play -- just like the original Simon. :-)
New Location
I am using the same development environment that I used when developing Fahrfall. That environment is very flexible and allows me to load code or data anywhere in the CoCo's memory, without regard for compatibility with the CoCo's built-in BASIC environment. Also, I have been arbitrarily loading code into an address above 16K -- higher than what many CoCo machines have.
I relocated the binary to location $0e00. This location is relatively low in a CoCo's memory, but it is high enough to avoid interfering with the CoCo's disk accesses. This should allow the game binary to load on either tape-based or disk-based CoCo systems. It definitely works for loading from tape (i.e. MP3 playback on my phone). I expect that it will work when loading from disk (or DriveWire) as well, but I haven't verified that just yet.
Unfortunately, the combination of that address and the length of the game binary is too high for CoCo machines with only 4K RAM. If that turns-out to be a limitation, then a "cassette only" version can easily be generated... ;-)
More Options
A few more options remain for development. An obvious one is to implement a keyboard interface so that people without the rotary controller (including emulator users) can play too. Support for the Dragon should be a no-op as-is, but the addition of keyboard support will require extra code to support both CoCo and Dragon keyboards. Also the current "you win" screen is a lame reward for someone that completes a 31 tone sequence -- perhaps something better could be done?
Further options could be modifications to the game itself. The original Simon had a couple of multi-player options available, as well as options for playback of the previous and longest completed sequences since starting the game. The AVRSimon project included other game options, including versions with no sound or no lights and a version where the tone matching was done in reverse order. Any or all of these might be possibilities for future development.
For now, I think that I will focus on mopping-up things for the Retrochallenge event. This will likely include the release of a git tree and some sort of wrap-up post in the next few days. For that, of course, you will have to stay tuned...
Feature Complete
I have now implemented all of the basic features of Simon. I can generate random tone sequences, play them for the user, monitor the user's control inputs, and evaluate those inputs against the generated sequence. I have added a timeout when monitoring the inputs. A slow or inactive user loses the game, as does one that fails to match the tone sequence. Losing the game results in a restart, while matching a maximum length sequence (31 tones) earns the player a congratulatory tone and a "you win" screen. FWIW, I also think that the game is fun to play -- just like the original Simon. :-)
New Location
I am using the same development environment that I used when developing Fahrfall. That environment is very flexible and allows me to load code or data anywhere in the CoCo's memory, without regard for compatibility with the CoCo's built-in BASIC environment. Also, I have been arbitrarily loading code into an address above 16K -- higher than what many CoCo machines have.
I relocated the binary to location $0e00. This location is relatively low in a CoCo's memory, but it is high enough to avoid interfering with the CoCo's disk accesses. This should allow the game binary to load on either tape-based or disk-based CoCo systems. It definitely works for loading from tape (i.e. MP3 playback on my phone). I expect that it will work when loading from disk (or DriveWire) as well, but I haven't verified that just yet.
Unfortunately, the combination of that address and the length of the game binary is too high for CoCo machines with only 4K RAM. If that turns-out to be a limitation, then a "cassette only" version can easily be generated... ;-)
More Options
A few more options remain for development. An obvious one is to implement a keyboard interface so that people without the rotary controller (including emulator users) can play too. Support for the Dragon should be a no-op as-is, but the addition of keyboard support will require extra code to support both CoCo and Dragon keyboards. Also the current "you win" screen is a lame reward for someone that completes a 31 tone sequence -- perhaps something better could be done?
Further options could be modifications to the game itself. The original Simon had a couple of multi-player options available, as well as options for playback of the previous and longest completed sequences since starting the game. The AVRSimon project included other game options, including versions with no sound or no lights and a version where the tone matching was done in reverse order. Any or all of these might be possibilities for future development.
For now, I think that I will focus on mopping-up things for the Retrochallenge event. This will likely include the release of a git tree and some sort of wrap-up post in the next few days. For that, of course, you will have to stay tuned...
Saturday, January 26, 2013
Still Moving
Another day, another opportunity for hacking! Actually, we had a little ice event here on Friday, so this particular Saturday was a great day to stay inside for some retro-hacking. Luckily, we had already stocked-up on bread and milk... :-)
Random Numbers
Simon is built around a randomly generated sequence of tones/colors that the player has to match to keep playing. However, the truth is that computers aren't really imaginative enough to generate truly random numbers. To compensate for that, there are a variety of common tricks that are used to generate seemingly random numbers through mathematical processes. The technique I used for generating random numbers in Fahrfall was a software implementation of an LFSR.
Perhaps surprisingly, Simon did something even simpler. Simon used a free running counter which was incremented every time the game entered its main loop. When it was time to extend the tone/color sequence, Simon simply used the two lowest-order bits of that counter in order to pick one of the four tone/color pairs (numbered 0-3). The inherent inconsistency of human timing makes it impossible to predict which number is in the machine's counter when these choices are made, resulting in something random enough to be useful for the game. This technique has worked well enough for Simon so far, so this is the technique I used in my implementation as well.
Getting Started
One thing I added to my code was a "start" screen. This gives the user more control over exactly when a game starts, and offers an opportunity for displaying copyright notices or perhaps some basic instructions. In this case, however, it provides another added benefit -- it gives the free running counter a chance to run for a while before the game starts. Without this chance for the counter to run, the game would always start with the same tone/color pair every time.
Follow The Leader
With all that said, the point of the random numbers is to generate a sequence of tone/color pairs. But having that sequence isn't worth much if you can't play it back for the game! I already had some code to play tones as the player selected each color. On top of that, I implemented some code to track the length of a sequence and to walk each member of it. For each member of the sequence, the corresponding tone is played and its color is highlighted. The tone playback is timed, and a short delay is inserted between each tone.
With that in place, I can move onto playing a generated sequence and adding the rest of the game logic. I expect to get that done over the next day or so. As always, stay tuned... :-)
Random Numbers
Simon is built around a randomly generated sequence of tones/colors that the player has to match to keep playing. However, the truth is that computers aren't really imaginative enough to generate truly random numbers. To compensate for that, there are a variety of common tricks that are used to generate seemingly random numbers through mathematical processes. The technique I used for generating random numbers in Fahrfall was a software implementation of an LFSR.
Perhaps surprisingly, Simon did something even simpler. Simon used a free running counter which was incremented every time the game entered its main loop. When it was time to extend the tone/color sequence, Simon simply used the two lowest-order bits of that counter in order to pick one of the four tone/color pairs (numbered 0-3). The inherent inconsistency of human timing makes it impossible to predict which number is in the machine's counter when these choices are made, resulting in something random enough to be useful for the game. This technique has worked well enough for Simon so far, so this is the technique I used in my implementation as well.
Getting Started
One thing I added to my code was a "start" screen. This gives the user more control over exactly when a game starts, and offers an opportunity for displaying copyright notices or perhaps some basic instructions. In this case, however, it provides another added benefit -- it gives the free running counter a chance to run for a while before the game starts. Without this chance for the counter to run, the game would always start with the same tone/color pair every time.
Follow The Leader
With all that said, the point of the random numbers is to generate a sequence of tone/color pairs. But having that sequence isn't worth much if you can't play it back for the game! I already had some code to play tones as the player selected each color. On top of that, I implemented some code to track the length of a sequence and to walk each member of it. For each member of the sequence, the corresponding tone is played and its color is highlighted. The tone playback is timed, and a short delay is inserted between each tone.
With that in place, I can move onto playing a generated sequence and adding the rest of the game logic. I expect to get that done over the next day or so. As always, stay tuned... :-)
Wednesday, January 23, 2013
Play It Again
The gears of progress creak forward once again! There is nothing very monumental to report, but at least now my CoCo sings when you work the controls...
Hip To Be Square
The CoCo actually has two potential sources of audio output. The higher quality output comes from the 6-bit DAC, which is capable of reproducing reasonably complex waveforms with the help of the CPU feeding it wave samples. This isn't terribly difficult to use, but it isn't terribly simple either. Plus, sharing the DAC for reading the controller further complicates its usage.
The other audio output on the CoCo is a 1-bit digital output used for generating square waves. This sort of output is very simple to use, and was quite common in devices of the same era as the CoCo. The tonal quality produced by this output isn't particularly pure or melodic, but it does have a certain richness to it. Plus, it happens to be the same technique used by the original Simon hardware.
Time Critical
Audio tones vary according to their frequency. The key to generating different frequencies is timing. The square wave output of the CoCo needs to be switched at intervals that are timed reasonably precisely in order to generate consistently accurate tones. But how?
Without external hardware, I can only think of three ways to do accurate timing on a CoCo: cycle counting; vertical sync; and horizontal sync. Instruction cycle counting is reliable, but it is difficult to apply to dynamic changes (e.g. playing different tones). Vertical sync occurs 60 times per second, only enabling generation of tones up to 30 Hz. That leaves leaves horizontal sync.
Horizontal sync on the CoCo fires 15720 times per second, enabling generation of tones up to 7860 Hz. While the audiophiles will be horrified at such a low sample rate, the truth is that this covers a very useful range of tones. Most importantly, it more than covers the range of tones required to play Simon.
Bugle Call
Legend has it that the creator of Simon sought long and hard for a set of tones that would always sound "in tune" no matter the order in which they were played. The tones played by Simon are said to have been inspired by the notes of a bugle. That may be so, but mimicking bugle tones doesn't quite match my vintage Simon game.
Luckily, some kind soul has gone through the trouble of reverse engineering the original Simon game and (more importantly) publishing that information for all to see. With that info I was easily able to tune my implementation to more closely match the original. That said, the bugle notes do sound a bit better -- I'm not sure which set of tones I will ultimately use!
Time is ticking, and I only have another week to finish-up. Hopefully I can find some time this weekend to implement the game logic, etc. To find-out how that goes, you'll have to stay tuned... :-)
Hip To Be Square
The CoCo actually has two potential sources of audio output. The higher quality output comes from the 6-bit DAC, which is capable of reproducing reasonably complex waveforms with the help of the CPU feeding it wave samples. This isn't terribly difficult to use, but it isn't terribly simple either. Plus, sharing the DAC for reading the controller further complicates its usage.
The other audio output on the CoCo is a 1-bit digital output used for generating square waves. This sort of output is very simple to use, and was quite common in devices of the same era as the CoCo. The tonal quality produced by this output isn't particularly pure or melodic, but it does have a certain richness to it. Plus, it happens to be the same technique used by the original Simon hardware.
Time Critical
Audio tones vary according to their frequency. The key to generating different frequencies is timing. The square wave output of the CoCo needs to be switched at intervals that are timed reasonably precisely in order to generate consistently accurate tones. But how?
Without external hardware, I can only think of three ways to do accurate timing on a CoCo: cycle counting; vertical sync; and horizontal sync. Instruction cycle counting is reliable, but it is difficult to apply to dynamic changes (e.g. playing different tones). Vertical sync occurs 60 times per second, only enabling generation of tones up to 30 Hz. That leaves leaves horizontal sync.
Horizontal sync on the CoCo fires 15720 times per second, enabling generation of tones up to 7860 Hz. While the audiophiles will be horrified at such a low sample rate, the truth is that this covers a very useful range of tones. Most importantly, it more than covers the range of tones required to play Simon.
Bugle Call
Legend has it that the creator of Simon sought long and hard for a set of tones that would always sound "in tune" no matter the order in which they were played. The tones played by Simon are said to have been inspired by the notes of a bugle. That may be so, but mimicking bugle tones doesn't quite match my vintage Simon game.
Luckily, some kind soul has gone through the trouble of reverse engineering the original Simon game and (more importantly) publishing that information for all to see. With that info I was easily able to tune my implementation to more closely match the original. That said, the bugle notes do sound a bit better -- I'm not sure which set of tones I will ultimately use!
Time is ticking, and I only have another week to finish-up. Hopefully I can find some time this weekend to implement the game logic, etc. To find-out how that goes, you'll have to stay tuned... :-)
Monday, January 21, 2013
Simple Simon
When last we met, I was just coming to peace with my rotary controller routines. A perfect solution probably would require either more hardware, more CPU time dedicated to sampling, or both. But, some experimentation with a clever software debounce algorithm and a pragmatic approach to handling illegal state transitions yielded empirical results that seemed acceptable for some sort of game. But what?
Get The Feel
A rotary (a.k.a. "spinner") control is a little unusual. It is constrained to a single degree of freedom, and the twisting mechanic is best suited to movement around a central point. Not just any game will lend itself to this dynamic, and there isn't much point in using a rotary control for a game that would be better suited to a more common control scheme.
I had intended to make some sort of tube shooter game for the CoCo3. I would still like to do that, but I don't think I can accomplish that during this month's Retrochallenge event. Consequently, I need something simpler to implement!
Simple Simon
I started thinking about simple games and round shapes. Before long, the answer became obvious -- Simon, an 80's classic. The game logic and basic mechanics (e.g. playing sounds) should be simple enough to implement in the allotted time. The rotary control might not seem like an obvious choice, but I think it will map well to Simon's four centrally arranged buttons.
Mock-Up
To get started, I knocked-up some code to draw four colored boxes on the screen. I refactored my controller code to buffer the movement indications a bit, so that turning the spinner resulted in controllable selections. I am using a white outline to indicate the currently selected color. Pressing the button changes the outline to match the color of the selected box, making a larger colored box. The larger colored boxes will also be used when the patterns are being played for memorization.
I think that covers the basic mechanics, other than playing the tones. That should be relatively simple -- YMMV, of course! I guess we'll find-out about that soon enough. Until then, stay tuned... :-)
Get The Feel
A rotary (a.k.a. "spinner") control is a little unusual. It is constrained to a single degree of freedom, and the twisting mechanic is best suited to movement around a central point. Not just any game will lend itself to this dynamic, and there isn't much point in using a rotary control for a game that would be better suited to a more common control scheme.
I had intended to make some sort of tube shooter game for the CoCo3. I would still like to do that, but I don't think I can accomplish that during this month's Retrochallenge event. Consequently, I need something simpler to implement!
Simple Simon
I started thinking about simple games and round shapes. Before long, the answer became obvious -- Simon, an 80's classic. The game logic and basic mechanics (e.g. playing sounds) should be simple enough to implement in the allotted time. The rotary control might not seem like an obvious choice, but I think it will map well to Simon's four centrally arranged buttons.
Mock-Up
To get started, I knocked-up some code to draw four colored boxes on the screen. I refactored my controller code to buffer the movement indications a bit, so that turning the spinner resulted in controllable selections. I am using a white outline to indicate the currently selected color. Pressing the button changes the outline to match the color of the selected box, making a larger colored box. The larger colored boxes will also be used when the patterns are being played for memorization.
I think that covers the basic mechanics, other than playing the tones. That should be relatively simple -- YMMV, of course! I guess we'll find-out about that soon enough. Until then, stay tuned... :-)
Sunday, January 13, 2013
Debouncing Distractions
It's been a while since I last posted, so I guess it is time for an update... I got a bit obsessed with debouncing the outputs from my controller, but never quite got it perfect. I eventually had to declare it a rathole and move onto something else. Nevertheless, I think I've made a little progress.
Debouncing
As I've noted, the rotary encoder I'm using (like all mechanical switches) is prone to "bounce" a bit as it changes state. This is a common problem, and there are tons of options for addressing it. I ruled-out using extra hardware, mostly to avoid the trouble of building the circuit into the controller. This leaves a variety of software options.
I started with a simple algorithm which read the controller status periodically. The algorithm insisted on three identical reads in a row before updating the "official" state of the controller. This worked reasonably well, and I used it to play around a bit with how often I read the controller and how many identical reads were required to update the state. Still, I was never quite satisfied with it.
I did some more research, and came across The Best Switch Debounce Routine Ever. I don't know if it really lives-up to it's name or not, but it was relatively simple to code in 6809 assembly language and it seems to work somewhat better than what I was doing before. That might be attributable to the fact that it also debounces the two inputs from the encoder independently (but simultaneously!), whereas my routine had considered them as a single 2-bit value. In any case, the new algorithm delivers reasonably stable results at what seems like a reasonable computational cost.
The problem is not completely solved, however, because I do still get some observable bounce and even some invalid state transitions. After reading Jack Ganssle's A Guide to Debouncing, I've concluded that the relatively slow clock speed of the CoCo's CPU and the demands of reading the rotary controller fast enough for playing a game means that I will not be able to filter-out all bounces. So, all I can hope for is that the effects of these anomalies will be minor enough to be reasonably ignored.
Another Demo
To determine whether or not ignoring controller anomalies is acceptable, I needed a demo that acted a bit more like a real game. I changed my demo code to use the data from the controller to move a cursor around the screen. This gave me a better "feel" for how something on the screen would react to a player's controller movements. After playing with that a bit, I am reasonably confident that enjoyable games can be made to use this controller. Now, someone just needs to write them! :-)
Ready Player One
I almost didn't get around to working on CoCo stuff this weekend, as I became immersed in a novel that I've started reading. Ready Player One is a really enjoyable sci-fi novel set in a dystopic future where humanity escapes the realities of a used-up world by living-out fantasies in a computer-generated virtual universe. The protagonist is involved in a contest within that virtual universe that requires those involved in it to be masters of 80's pop culture and trivia, with a definite slant towards sci-fi, RPGs, classic arcade games, and other things geeks love. As if this wasn't catnip enough for an old geek like me, the fictional creator of the virtual universe got his start on a CoCo2. Moreover, one of the key points of the plot involves playing Dungeons of Daggorath on a CoCo2 inside that virtual universe! I can hardly put it down...
Anyway, I'll be travelling some this week. So, I probably won't be updating the blog any more frequently this week than the last. Oh, well...I'm sure you will all live through it! But, I do hope that I can knock-up something interesting to post here by next weekend or so. Until then, stay tuned...
Debouncing
As I've noted, the rotary encoder I'm using (like all mechanical switches) is prone to "bounce" a bit as it changes state. This is a common problem, and there are tons of options for addressing it. I ruled-out using extra hardware, mostly to avoid the trouble of building the circuit into the controller. This leaves a variety of software options.
I started with a simple algorithm which read the controller status periodically. The algorithm insisted on three identical reads in a row before updating the "official" state of the controller. This worked reasonably well, and I used it to play around a bit with how often I read the controller and how many identical reads were required to update the state. Still, I was never quite satisfied with it.
I did some more research, and came across The Best Switch Debounce Routine Ever. I don't know if it really lives-up to it's name or not, but it was relatively simple to code in 6809 assembly language and it seems to work somewhat better than what I was doing before. That might be attributable to the fact that it also debounces the two inputs from the encoder independently (but simultaneously!), whereas my routine had considered them as a single 2-bit value. In any case, the new algorithm delivers reasonably stable results at what seems like a reasonable computational cost.
The problem is not completely solved, however, because I do still get some observable bounce and even some invalid state transitions. After reading Jack Ganssle's A Guide to Debouncing, I've concluded that the relatively slow clock speed of the CoCo's CPU and the demands of reading the rotary controller fast enough for playing a game means that I will not be able to filter-out all bounces. So, all I can hope for is that the effects of these anomalies will be minor enough to be reasonably ignored.
Another Demo
To determine whether or not ignoring controller anomalies is acceptable, I needed a demo that acted a bit more like a real game. I changed my demo code to use the data from the controller to move a cursor around the screen. This gave me a better "feel" for how something on the screen would react to a player's controller movements. After playing with that a bit, I am reasonably confident that enjoyable games can be made to use this controller. Now, someone just needs to write them! :-)
Ready Player One
I almost didn't get around to working on CoCo stuff this weekend, as I became immersed in a novel that I've started reading. Ready Player One is a really enjoyable sci-fi novel set in a dystopic future where humanity escapes the realities of a used-up world by living-out fantasies in a computer-generated virtual universe. The protagonist is involved in a contest within that virtual universe that requires those involved in it to be masters of 80's pop culture and trivia, with a definite slant towards sci-fi, RPGs, classic arcade games, and other things geeks love. As if this wasn't catnip enough for an old geek like me, the fictional creator of the virtual universe got his start on a CoCo2. Moreover, one of the key points of the plot involves playing Dungeons of Daggorath on a CoCo2 inside that virtual universe! I can hardly put it down...
Anyway, I'll be travelling some this week. So, I probably won't be updating the blog any more frequently this week than the last. Oh, well...I'm sure you will all live through it! But, I do hope that I can knock-up something interesting to post here by next weekend or so. Until then, stay tuned...
Monday, January 7, 2013
Rotary Visuals
I spent a little time on Sunday trying to get some code written that is more specific to the rotary controller. I started with some existing code snippets for reading the CoCo's joystick inputs, and some basic video mode setup as well as other initializations. It seems like that should have been simple, but I hit a bit of a snag...
That Ain't Right
I implemented a simple program to read the rotary controller by polling the joystick port in a tight loop. I implemented the program for the CoCo3, and used a CoCo3-specific video mode. After clearing the screen, I used the inputs from the controller to form a bitmask which I then used to change the color for palette entry 0. This was intended to provide some basic feedback as a first step before going forward.
Unfortunately, I quickly noticed that I was only seeing three colors when I turned the knob on the controller. I tweaked the code a bit, only to find that in some cases I was down to only two colors in the sequence. After re-verifying that the controller hardware still worked with the earlier demo (on a CoCo1), I even tried a different CoCo3. Still, the issue remained. In an effort to verify the software itself, I transformed the program into something that would run on any CoCo. Then I could test the program on all the same hardware that ran the other demo.
Back To Basics
I changed to using a text mode for the output. Instead of changing a color, I used the bitmask derived from the controller inputs to generate a number character on the screen. This allowed me to better understand the exact sequence of values being detected by the software.
At this point, I just fiddled with the controller and watched the numbers change. After a while, I switched from the rotary controller to the standard joystick instead. Eventually I was able to understand what I was seeing -- the second axis almost always read the same as the first axis, with some exceptions depending on a few tweaks here and there.
Wait For It
The CoCo reads the joysticks using a DAC and a comparator. One of the comparator inputs is selected using a linear switch controlled by one of the CoCo's PIAs. In order to read two axes, the switch has to be changed to select the second axis. In my existing code, switching the axis is followed by setting a new DAC value before checking the comparator.
The rotary controller outputs are digital, so only one comparator check is needed per access. Also, the same DAC value is used for the comparisons on both axes. Consequently, I was checking the comparator immediately after switching to the second axis. This seems to work fine on the CoCo1, but on the CoCo3 it is prone to failure. Inserting a few NOP instructions seems to stabilize that situation. A dummy set of the DAC value would probably give enough time for the comparator to stabilize as well.
With that issue resolved, I extended my test program to use color and more screen real estate. This makes it easier to visualize and track the states as they are read from the rotary controller. It also confirms that switch bounce is a problem, as demonstrated in the video clip above. Figuring-out how to deal with that will probably be my next step -- stay tuned!
That Ain't Right
I implemented a simple program to read the rotary controller by polling the joystick port in a tight loop. I implemented the program for the CoCo3, and used a CoCo3-specific video mode. After clearing the screen, I used the inputs from the controller to form a bitmask which I then used to change the color for palette entry 0. This was intended to provide some basic feedback as a first step before going forward.
Unfortunately, I quickly noticed that I was only seeing three colors when I turned the knob on the controller. I tweaked the code a bit, only to find that in some cases I was down to only two colors in the sequence. After re-verifying that the controller hardware still worked with the earlier demo (on a CoCo1), I even tried a different CoCo3. Still, the issue remained. In an effort to verify the software itself, I transformed the program into something that would run on any CoCo. Then I could test the program on all the same hardware that ran the other demo.
Back To Basics
I changed to using a text mode for the output. Instead of changing a color, I used the bitmask derived from the controller inputs to generate a number character on the screen. This allowed me to better understand the exact sequence of values being detected by the software.
At this point, I just fiddled with the controller and watched the numbers change. After a while, I switched from the rotary controller to the standard joystick instead. Eventually I was able to understand what I was seeing -- the second axis almost always read the same as the first axis, with some exceptions depending on a few tweaks here and there.
Wait For It
The CoCo reads the joysticks using a DAC and a comparator. One of the comparator inputs is selected using a linear switch controlled by one of the CoCo's PIAs. In order to read two axes, the switch has to be changed to select the second axis. In my existing code, switching the axis is followed by setting a new DAC value before checking the comparator.
The rotary controller outputs are digital, so only one comparator check is needed per access. Also, the same DAC value is used for the comparisons on both axes. Consequently, I was checking the comparator immediately after switching to the second axis. This seems to work fine on the CoCo1, but on the CoCo3 it is prone to failure. Inserting a few NOP instructions seems to stabilize that situation. A dummy set of the DAC value would probably give enough time for the comparator to stabilize as well.
With that issue resolved, I extended my test program to use color and more screen real estate. This makes it easier to visualize and track the states as they are read from the rotary controller. It also confirms that switch bounce is a problem, as demonstrated in the video clip above. Figuring-out how to deal with that will probably be my next step -- stay tuned!
Saturday, January 5, 2013
Smoke Test
Well, it seems I've been a bit lazy so far. With the new year came the end of my Christmas vacation, and so I had to pay a bit more attention to work, etc. I'm sure I'm not the only one!
Despite the lack of real progress, I thought that it might be worthwhile to demonstrate the workings of the rotary controller. So, I posted a video of me stepping the controller through its paces using the joystick demo code I originally wrote while developing Fahrfall.
CoCo Joysticks
I'm using the joystick port on the CoCo to read the rotary controller. That seems obvious enough, but the output from the rotary encoder is digital and the CoCo joystick inputs are analog. I resolved that discrepancy using pull-up resistors, forcing the joystick inputs to always read either high or low (and not in between). Given that understanding, the basic process for reading those inputs is the same as I described in the Fahrfall blog. In this case we should be able to do only one value check per axis, since there won't be any "in between" readings.
Debounce
The rotary encoder I am using is mechanical. Like all mechanical switches, there will tend to be some "bounce" when the contacts close. It is possible to do switch debouncing with hardware, but it complicates the electrical design and construction. Consequently, I didn't add any such hardware.
I'm not sure if debounce is usually an issue with game controllers or not. In most cases, false readings probably just work themselves out with the next read. But with the quadrature signal coming from the rotary controller, the sequence of values becomes important. This is especially true if we were to miss a transitional state.
I found several references to people having switch bounce problems when using rotary controllers with Arduino projects. I don't know if any of the techniques they use to fight switch bounce will be helpful on the CoCo or not, but I suspect that I may have to do something to account for switch bounce in software.
Keeping Up
A related problem with the rotary encoder will be the sampling rate. The encoder I am using is rated for 24 pulses per revolution. That number represents the number of full cycles per channel in a given revolution. But since the signals are bound together using quadrature encoding, the real number of transitions to consider is 4x that, or 96 transitions per revolution.
The most common rate for sampling joysticks in an arcade game is around 60Hz. It seems reasonable to suspect that someone could turn the controller knob more than 2/3 of a revolution during a second! So, the sampling will have to happen more frequently. If a game plays sound, there will need to be a routine running at a rate of several kHz in order to feed the DAC for audio output. Maybe piggy-backing on that routine to read the controller input will be good enough to keep pace? It might even fix the debounce issue for free?
Well, I guess we aren't going to find-out today... I'll try to knuckle down on this and report back in a day or two. Until then, stay tuned! :-)
Despite the lack of real progress, I thought that it might be worthwhile to demonstrate the workings of the rotary controller. So, I posted a video of me stepping the controller through its paces using the joystick demo code I originally wrote while developing Fahrfall.
CoCo Joysticks
I'm using the joystick port on the CoCo to read the rotary controller. That seems obvious enough, but the output from the rotary encoder is digital and the CoCo joystick inputs are analog. I resolved that discrepancy using pull-up resistors, forcing the joystick inputs to always read either high or low (and not in between). Given that understanding, the basic process for reading those inputs is the same as I described in the Fahrfall blog. In this case we should be able to do only one value check per axis, since there won't be any "in between" readings.
Debounce
The rotary encoder I am using is mechanical. Like all mechanical switches, there will tend to be some "bounce" when the contacts close. It is possible to do switch debouncing with hardware, but it complicates the electrical design and construction. Consequently, I didn't add any such hardware.
I'm not sure if debounce is usually an issue with game controllers or not. In most cases, false readings probably just work themselves out with the next read. But with the quadrature signal coming from the rotary controller, the sequence of values becomes important. This is especially true if we were to miss a transitional state.
I found several references to people having switch bounce problems when using rotary controllers with Arduino projects. I don't know if any of the techniques they use to fight switch bounce will be helpful on the CoCo or not, but I suspect that I may have to do something to account for switch bounce in software.
Keeping Up
A related problem with the rotary encoder will be the sampling rate. The encoder I am using is rated for 24 pulses per revolution. That number represents the number of full cycles per channel in a given revolution. But since the signals are bound together using quadrature encoding, the real number of transitions to consider is 4x that, or 96 transitions per revolution.
The most common rate for sampling joysticks in an arcade game is around 60Hz. It seems reasonable to suspect that someone could turn the controller knob more than 2/3 of a revolution during a second! So, the sampling will have to happen more frequently. If a game plays sound, there will need to be a routine running at a rate of several kHz in order to feed the DAC for audio output. Maybe piggy-backing on that routine to read the controller input will be good enough to keep pace? It might even fix the debounce issue for free?
Well, I guess we aren't going to find-out today... I'll try to knuckle down on this and report back in a day or two. Until then, stay tuned! :-)
Tuesday, January 1, 2013
CoCo Rotary Controller
In the early days of video games, there was a lot of variety in game controllers. While plenty of games just used a joystick and a button or two, a number of games in the arcades had more interesting control mechanisms. The Lunar Lander thrust controller, the Centipede trackball, the Battlezone dual control sticks, and the Star Wars flight yoke all come to mind as examples of how the controller itself can be a part of the overall gaming experience. But for my money, few controllers matched their games as well as the rotary controller did for Tempest.
Inspiration
Tempest was a great game -- so much so, that Atari had it remade as Tempest 2000 and released for the Jaguar back in the early 90's. Tempest 2000 works perfectly well with the D-pad, but it turns-out that Jeff Minter also added support to Tempest 2000 for rotary controllers. The problem, of course, is that Atari never made such a controller...
I knew about the rotary controller support in Tempest 2000 when I bought it off the shelf 'back in the day'. I had always intended to build such a controller, but only recently got around to doing that -- more on that in some later post! Anyway, as part of that journey I ended-up with a few random extra parts. In particular, I had an extra rotary encoder.
Making Something Beautiful
From an electrical standpoint, joysticks and most other controllers are fairly simple. The real problem isn't wiring them, it is building something to hold those buttons, sticks, or whatever in a form that makes them usable. Often the simplest solution is to repurpose something designed to do a similar job into something that works more as one wishes.
The original TRS-80 joysticks that Tandy offered for use with the CoCo were awkward and are generally unloved. The best I can say about them is that they seem a bit less poorly designed than the ones made for the Dragon or the BBC Micro. :-) Nevertheless, they do offer a plastic shell with some rudimentary ergonomics, an available button, accommodations for wiring, and a pre-drilled hole ready for mounting the shaft of a rotary encoder.
At some point, an idea became obvious -- take the guts out of an old CoCo joystick and turn it into a rotary controller!
Getting It To Work
The procedure was relatively simple. First, disconnect the wires from the joystick gimbal and remove it from the housing. Next, mount the rotary encoder in the now vacant hole in the housing. Once the wiring is complete (see below), then install a knob that suits your tastes. That completes the mechanical installation!
For the wiring, solder a 10k resistor to each of the signal outputs from the encoder. Tie the other ends of those resistors to the +5V line from the CoCo. The common input on the encoder gets tied to Ground from the CoCo, as does one of the button terminals. The other button terminal connects to the button line from the CoCo, as expected. The X and Y lines from the CoCo are each tied directly to one of the signal outputs from the encoder. The connections are made so that in operation the X and Y lines alternate between +5V and Ground. Turning the encoder clockwise will cause (X,Y) to follow the sequence of (0,0) -> (0,5) -> (5,5) -> (5,0). Likewise, turning counter-clockwise will change the sequence to (0,0) -> (5,0) -> (5,5) -> (0,5).
Hopefully that description is clear? The circuit really is simple. I'll try to find some time to draw-up a little schematic to post. If I don't manage to do so and you want to build one, feel free to contact me if you can't figure-out the schematic. :-)
In any case, building the hardware is only the first step. This controller works a bit differently than the existing joysticks. Consequently, there is no software to make any sense of it. So, starting to figure that out will be the next step. As always, be sure to stay tuned!
Inspiration
Tempest was a great game -- so much so, that Atari had it remade as Tempest 2000 and released for the Jaguar back in the early 90's. Tempest 2000 works perfectly well with the D-pad, but it turns-out that Jeff Minter also added support to Tempest 2000 for rotary controllers. The problem, of course, is that Atari never made such a controller...
I knew about the rotary controller support in Tempest 2000 when I bought it off the shelf 'back in the day'. I had always intended to build such a controller, but only recently got around to doing that -- more on that in some later post! Anyway, as part of that journey I ended-up with a few random extra parts. In particular, I had an extra rotary encoder.
Making Something Beautiful
From an electrical standpoint, joysticks and most other controllers are fairly simple. The real problem isn't wiring them, it is building something to hold those buttons, sticks, or whatever in a form that makes them usable. Often the simplest solution is to repurpose something designed to do a similar job into something that works more as one wishes.
The original TRS-80 joysticks that Tandy offered for use with the CoCo were awkward and are generally unloved. The best I can say about them is that they seem a bit less poorly designed than the ones made for the Dragon or the BBC Micro. :-) Nevertheless, they do offer a plastic shell with some rudimentary ergonomics, an available button, accommodations for wiring, and a pre-drilled hole ready for mounting the shaft of a rotary encoder.
At some point, an idea became obvious -- take the guts out of an old CoCo joystick and turn it into a rotary controller!
Getting It To Work
The procedure was relatively simple. First, disconnect the wires from the joystick gimbal and remove it from the housing. Next, mount the rotary encoder in the now vacant hole in the housing. Once the wiring is complete (see below), then install a knob that suits your tastes. That completes the mechanical installation!
For the wiring, solder a 10k resistor to each of the signal outputs from the encoder. Tie the other ends of those resistors to the +5V line from the CoCo. The common input on the encoder gets tied to Ground from the CoCo, as does one of the button terminals. The other button terminal connects to the button line from the CoCo, as expected. The X and Y lines from the CoCo are each tied directly to one of the signal outputs from the encoder. The connections are made so that in operation the X and Y lines alternate between +5V and Ground. Turning the encoder clockwise will cause (X,Y) to follow the sequence of (0,0) -> (0,5) -> (5,5) -> (5,0). Likewise, turning counter-clockwise will change the sequence to (0,0) -> (5,0) -> (5,5) -> (0,5).
Hopefully that description is clear? The circuit really is simple. I'll try to find some time to draw-up a little schematic to post. If I don't manage to do so and you want to build one, feel free to contact me if you can't figure-out the schematic. :-)
In any case, building the hardware is only the first step. This controller works a bit differently than the existing joysticks. Consequently, there is no software to make any sense of it. So, starting to figure that out will be the next step. As always, be sure to stay tuned!