Prof. Dr. rer. nat. Peer Johannsen

Weird Science - Tales from the Vectrex Academy Lab

Vectrex Project Title

  • JOY_ANALOG

Project Status

  • Ongoing

Synopsis

  • An investigation of the Vectrex BIOS routine Joy_Analog ($F1F5)
  • Analysis of the precision of the analog joystick resolution (Vec_Joy_Resltn, $C81A)

Results

  • The BIOS Joy_Analog() routine computes signed 8-bit values for the analog Y and X positions of the joystick of the Vectrex controller.
  • The resolution of the resulting values can be set by modifying the value of the BIOS variable Vec_Joy_Resltn ($C81A). Only powers of two (one-hot encoded bit-patterns) and the value zero are allowed here. The lower the value, the finer the resolution will be.
  • Some sources to be found on the internet say "0x80 is least accurate, and 0x00 is most accurate." However, a value of 0x80 causes the routine not to finish and to stay within an infinite loop.
  • That loop is not a bug. The official WT "Vectrex Programmer's Manual Volume 2" calls Vec_Joy_Resltn by the name of POTRES and states only the following allowed values:

       POTRES = Joystick resolution limit, where
      $00 – 8 bits (default)
      $01 – 7 bits
      $02 – 6 bits
      $04 – 5 bits
      $08 – 4 bits
      $10 – 3 bits
      $20 – 2 bits
      $40 – 1 bit
  • These values correspond to the following resulting resolutions of the computed X/Y values:

            Vec_Joy_Resltn    minimum value    maximum value      resolution delta
        0b00000000        -128             +127               1
        0b00000001        -128             +126               2
        0b00000010        -128             +124               4
        0b00000100        -128             +120               8
        0b00001000        -128             +112               16
        0b00010000        -128             +96                32
        0b00100000        -128             +64                64
        0b01000000        -128             0                  128

  • So each resolution should give you the complete range from minimum to maximum in steps of delta when moving the joystick completely from one end of an axis to the other end.
  • In a post on the Vector Gaming Forum, Graham Toal wrote that he had observed that joystick values still only increment or decrement only in steps of 4, despite using the maximum resolution setting. He approached me and suggested a deeper investigation of this phenomenon, and that is what I did.
  • On my machine I also got values changing only in steps of 4 when slowly moving the joystick, but not always. Most of the times, the delta was 4, but at some rare but specific steps the delta was 2, or even the desired 1. An analysis of the BIOS source code, and some heavy testing then revealed the following:
  • The reason for this behavior seems to be a timing issue, or more precisely, a DAC delay issue, or a delay issue of the comparator circuit which is used by the VIA.
  • Due to the design of the Vectrex hardware, the actual values of the joystick potentiometers cannot be read directly. Theirs values can only be compared to a value which is stored to the DAC, and the VIA reports whether the result of such a comparison is "lower" or "greater or equal".
  • Joy_Analog implements an iterative approximation procedure which repeatedly compares its derived value to the actual value of the joystick, by always guessing the middle value of the remaining interval (bisection method). Thus, in 8 steps (iterations), each value of a full range of 256 values (1 Byte) can precisely be analyzed. With fewer iterations, the resolution gets coarser as indicated by the tables above.
  • Once the current value, which is to be compared to the actual joystick input, is stored to the DAC, it takes some cycles until the result, which is returned by the comparator, becomes stable and reliable. The BIOS routine does not use any explicit wait-cycles here:

       sta     <VIA_port_a     ; store value to be compared to the DAC
      lda     #0x20           ; select comparator
      bita    <VIA_port_b     ; get comparator result


    The effect seems to be that sometimes an iteration incorrectly gets the result from the comparison of the previous iteration. And that causes some bits of the computed joystick position to be wrong, and the overall result to be somewhat fuzzy. The higher the chosen resolution, the more frequent and prominent these inaccuracies become (this is inherent to the Joy_Analog algorithm).

    I do not know on exactly what the specific delay depends (DAC, VIA, mode, manufacturer, age, ...?). For my machine, I had to introduce a whopping delay of 18(!) cycles, until everything worked fine.

      sta     <VIA_port_a     ; store value to be compared to the DAC
      tfr     a,a             ; 6 cycles nop
      tfr     a,a             ; 6 cycles nop
      tfr     a,a             ; 6 cycles nop
      lda     #0x20           ; select comparator
      bita    <VIA_port_b     ; get comparator result


    I do not think that 18 cycles are always necessary. The effect caused by this DAC delay (or comparator delay) seems to depend on the number of bits flipped when storing a new value to the DAC (compared to its previous value). But I had to go up to 18 cycles to make the whole thing always work reliably.
  • I have patched my BIOS accordingly, and now I always get the full 8-bits resolution for the joystick values when setting maximum resolution, and I also get the correct values for lower resolutions.
  • There are not many games which actually use the analog joystick mode, and whether or not the effects of such a patch become noticeable might vary from Vectrex to Vectrex, depending on the hardware and the state it is in. In my case I have so far found two examples where I could see a difference.
  • Hyperchase is infamous for its delicate analog sideways steering of the car. It uses Joy_Analog with maximum resolution, and with the patch the car now moves a tiny bit more smoothly from side to side.
  • Starhawk, in analog joystick mode, uses a value of 4 for Vec_Joy_resltn. With the patch, I now get exactly the corresponding reduced resolution, without the occasional fuzziness. However, this somehow makes the gameplay feel worse. I guess that the previous fuzziness of the computed joystick values here actually added to the benefit of playability. I modified Starhawk to use maximum resolution 0 for Vec_Joy_resltn, and now, with the patched BIOS, I get a cross hair moving really smoothly across the complete screen.
  • The downside of the patch, of course, is that Joy_Analog now eats up even more cycles than it already does.

Trivia

  • The way the approximation algorithm (iterated bisection) is implemented in Joy_Analog is actually quite clever and short. Really nice job of the original programmers!

Update

  • Instead of using an artificial delay of 18 cycles in two places of the approximation algorithm (total delay of 36 cycles), I now use a delay of 26 cycles in just one place. That seems to work reliably, at least on my machine.
  • I have also optimized several other parts of the code, and since Joy_Analog shares some parts of this code with Joy_Digital, the side-effect is a slight speed-up of Joy_Digital. Some numbers:

      estimated cycle count of Joy_Digital (original):                     92 + ~224 * n 
      estimated cycle count of Joy_Analog (maximum resolution, original):  99 + ~479 * n

      estimated cycle count of Joy_Digital (optimized):                    86 + ~222 * n
      estimated cycle count of Joy_Analog (maximum resolution, optimized): 93 + ~664 * n


    where n is the number of results to compute (between 0 and 4). So we see that the fix for getting the optimum analog resolution comes at a price.
  • The optimized code is one byte shorter than the original and nicely fits into the respective ROM space.

Author

  • Peer Johannsen

Test Program Screenshots

Downloads

  • The program uses several (custom) Joy_Analog routines.
  • It will show you the current x value of controller 1 (in decimal and in binary), and the current joystick resolution (binary only).
  • Press buttons 1 and 2 to decrease or increase the resolution (caveat: do not use value 0b10000000, see above).
  • Press button 4 to cycle through the different versions of the Joy_Analog routine. This allows you to switch between the resident BIOS of your machine, BIOS B796/7931 (both have the same Joy_Analog code), BIOS 7adb (there is a slight change in the code of Joy_Analog in this BIOS version which might give some better results), and my patched Joy_Analog routine with additional wait cycles.
  • Slowly move the joystick from end to end and see what values and actual resolution you get.
  • I would be interested to learn if you also get the complete range and the correct values for each resolution using my 18-cycles patch, or if there might be more cycles necessary, so please let me know.
  • Downloads are free and for non-commercial use only. Use at your own risk.
  • Please respect the copyright and credit the author and the origin of this project.

Latest modification on 05/15/2024, 14:15
  • Update added