Tuesday, August 21, 2018

Apple II 4joy - Serial interfacing through the game port

The Apple II 4Joy is an adapter that lets you connect four digital joysticks to the Apple II via the game port.

In my last blog I looked at running a digital joystick interface via the IIc's serial port. Technically I wasn't correct in saying that the serial interface is the only available option for communication on the IIc. Obviously there are other options such as the floppy disk interface, the keyboard interface and the game port interface but these are most likely already being used for dedicated functions. The game port however can be used for general communications but it's only unidirectional. In our case this is enough to get by on. I evaluated a few methods of communicating using this interface.

Method 1: The first method I looked into involved transferring a byte of information by generating a resistive circuit that mimics an Apple II paddle / joystick axis. This could be done by outputting eight digital lines from the microcontroller and switching parts of a resistive ladder to form the zero to 150k ohm resistance range needed by the Apple II. Alternately an extra chip such as the AD5206 could be used to generate the resistance. This method would require multitasking to put through four digital joystick status bytes over the IIc's only two paddle lines and each Apple II would need to be tuned because the game port's analog circuit contains discrete components that vary slightly from one machine to another. The other factor is that reading analog values via the Apple IIs games port is very slow. Even if the format is modified to be limited to a low analog value I estimate it would still take about 2000 microseconds to read, process and store the four joystick status values. This method is not preferred for serial data communications.

Method 2: This method involves running digital signals though the analog paddle lines. Each analog port would be reduced to a value of zero ("False") and non-zero ("True"). Reading an analog value from the paddle port takes about 4 microseconds if the value is zero all the way up to about 3000 microseconds if the value is 255. If the data is digital then we can read the port after 4 microseconds and get an answer straight away. This translates into a transfer rate of about 200 microseconds per byte instead of up to 3000. Most Apple IIs have four paddle lines (PDL0, PLD1, PDL2 and PLD3) so we could send though a nibble of data at a time (this is not as great as it sounds because you still need to read each bit at a time). The push button lines could be used to handle the synchronisation. The software would require some overheads in putting the two nibbles and the format back together but this would be straight forward. The hardware would require something like a 4066 chip to switch each analog port from 0k resistance to about 1.5k or greater. This should result in enough time to read all four analog ports and determine if each is "True" or "False". The IIc however does not have PDL2 and PLC3 lines. These were removed to make way for the mouse interface. Instead these lines are TTL compatible digital input lines (XDIR and YDIR). We don't need to change our method by much because we are already dealing with digital signals. The only change needed would be to add a toggle switch which allows the microcontroller to connect to XDIR and YDIR directly on the IIc or to the 4066 chip for non-IIc Apples. The result of all this would be to have all four joystick status bytes sent over in about 1000 microseconds. This is not a bad solution but can we do better? 

Method 3: The last method I looked at was to send a serial stream of bits though the game port's push button lines. One push button would be used for data and the other would be used for synchronisation. It's unlikely that the microcontroller's clock rate will be a direct multiple of the Apple II's clock rate so synchronisation needs to happen quite often, possible once every byte. A byte could be sent through in under 100 microseconds. The software would be relatively simple (simpler than the above methods). The routine to do the synchronisation, data transfer, processing and storage would not be as compact as running data through the serial port but should result in a much faster transfer rate. With this setup the hardware becomes simple too. Only the microcontroller is needed and a few discrete components. The result of all this is being able to transfer the four joystick status bytes in around 500 microseconds.

Once I looked at method three it was clear that this was going to be the best option to tackle. A good amount of the work was already done in the serial port project so I reused as much as I could. In terms of hardware the TTL to RS232 converter was removed and in its place I added a small breadboard with the DE9 pin plug, some discrete components and some headers for testing. This project was all about getting the software right which meant timing was critical and counting cycles on the Apple II as well as the microcontroller was needed.

Showing microcontroller output is too fast and then after delays have been added.

The first step was to write the fastest read loop on the Apple II. After obtaining a few pointers from Michael's NadaNet project, I was able to nut this out. This was going to be the bottle neck since the microcontroller was capable of outputting data much quicker than the Apple II could read it. The microcontroller code was written to stream a sync signal followed by an eight bit byte for each joystick. The dilemma now was how to view what the microcontroller was outputting and compare that to when the Apple II was expecting a read. The timing was sub-microsecond critical and the Apple II was reading (inputting) signals and not outputting them. The easiest thing to do was to move the development from the Apple IIc over to the Apple IIe. What this allowed me to do was to take a copy of the code, change the read instructions into write instructions but at the same time keep the cycle timing the same. These write instructions were then sent to the IIe's 16pin game port output (annunciator) pin. By adding this output to the logic analyser I was able so see exactly when the Apple II was going to be performing a read operation. I could then tune the microcontroller outputs to match the requirements of the Apple II.

Since the joystick number was going to be extracted from the data (just like in the serial port version) there was no need to synchronise starting at a specific byte. So long as four bytes were read in a row it did not matter which joystick status byte was read first.

In tuning the interface there were several critical points to get right:

1. Apple II read pulse length.

To get the microcontroller to match the Apple II, read pulse lengths measurements from the logic analyser were used and delays were added in the appropriate places. The faster the microcontroller can be run the more resolution is available to match the Apple II. Since one speed is not a subset of the other then the best we can ever do is an approximation.

2. Sync pulse length.

The Apple II code that checks for the sync pulse is seven cycles long. Therefore the sync pulse length needs to be at least seven cycles long but less than twice that.

3. Time between bytes.

Once the Apple II has read a byte of data the microcontroller needs to pause for while until the Apple II catches up with processing and storing that data. For this project that amount was roughly forty five microseconds.

We can see from the above screen shot that if the sync pulse is too short or if the delay between bytes is too short then a gap is generated between the joystick status bytes. This condition would be too short to affect any game play but it is undesirable and easy to eliminate by performing more tuning.

4. Time between the start of sync and the start of first read.

These screen shots show how much the time between the start of the sync pulse and the start of the read pulse can vary.

The last critical point was to fine tune the time between the start of the sync pulse and the start of the first read. This is where all the fun began. This start time variance is determined by the length of the sync check.

The sync check takes seven cycles to execute and looks like this

LOOP CPX $C061 - 4 cycles
   BCS LOOP - 3 cycles on jump

however the initial read loop takes six cycles and looks like this

   CPX $C062 - 4 cycles
   ROL - 2 cycles

Therefore the read pulse is going to be shorter than the variance of the start time. In other words this is not going to work. The read pulse needed to be extended. The longer the read pulse is, the less significant the start time variance becomes but also at the same time this slows down communications. By adding just one extra delay into the read loop it takes the cycle count to eight which is larger than the sync check.

Even though the sync check pulse is now less than the read pulse when you take into account that the microprocessor timings are not directly linked to the Apple II clock, this becomes our border line condition. I can get communications working at this speed but it's not stable. It can be tuned to a given computer but the aim is to have the device working on any Apple II computer. This translates into a communication rate of around 80kbps. This includes reading the data, processing the data (extracting the joystick number) and storing the data.

Testing for stability was quite easy to do. The microcontroller was setup to transmit a specific byte. The Apple II side was setup to read this byte and if it was different from what was expected then a counter was incremented. As the timing was adjusted on the microcontroller you could see the rate at which the counter was ticking over slowed down until it stopped counting. It had reached stability.

To make the interface stable I ended up expanding the read loop, not by adding an extra NOP instruction but by making the read routine into a loop (instead of just having the same read code duplicated for every bit). This resulted in a read pulse of eleven cycles but it makes the code a lot shorter. A good compromise in my book. This translates into a communication rate of around 60kbps which is still six times faster than reading analog data or several times faster than what I was able to achieve through the serial port. For processing joystick data it is still an overkill.

At first I thought that running communications over the two game port push button lines would be the perfect solution especially in making a device that would work on all models of the Apple II. Well it didn't turn out as straight forward as I had hoped. These were the issues:-

1. Apple added filtering capacitors to the Apple IIe platinum model so I suspect the 4Joy in its current setup will not work on this machine. Without removing the filtering capacitors the only option would be to slow down the communication rate. I don't have one of these models to test on.

2. I've had mixed results trying to get these high speed communications going over the push button lines on the Apple IIc. Sometimes it works but then other times it does not. I suspect this is to do with the resistor pack on the IIc's game port interface. I tried replacing the general signal diodes with ones that have a lower breakdown voltage however this made the timings worse. On the IIc I found that diverting the communications from the PB0/PB1 pins over to the XDIR/YDIR pins bypasses the resistor pack and makes the interface work very well (similar to my tests on a IIe and a IIgs). The other option would be to slow down the communication speed.

3. The game port push button pins (PB0 and PB1) on some Apple II models are tied into the reboot function and the self-diagnostic function. Since the sync pulse on time is much less then the data on time I swapped my PB0 with PB1 signals so that the probability of starting the self-diagnostic function on a control-reset is very low. It does happen occasionally but I get around this by doing a second control-reset. However this means that the probability of getting a reboot from doing a control-reset is quite high. I prefer it to be this way around than the reverse.

The next phase will be to add USB support and combine it with the "Bluetooth Game Port Receiver". With a flick of a switch it should be possible to swap from being a single USB/Bluetooth analog joystick receiver to a multi USB/Bluetooth digital joystick solution.

All the project files are contained here. https://docs.google.com/open?id=1CGB9NA_eKHfWACUQh_alhHitOMYklRSN