Thursday, February 20, 2014

Star Rider's steering mechanism

As some of you may know, Star Rider had a motorcycle-styled steering mechanism.  From the CPU's point of view, this steering mechanism is digital and is composed of 6 bits.




Unfortunately, I was unable to see anywhere in the schematics that described what each of these 6 bits (ST0-ST5) actually mean.

Fortunately, Star Rider has a rather robust suite of diagnostics tests, one of which tests the steering mechanism.  Ah ha!  I decided to use this to figure it out by trial and error.  After passing in some guesses for what the bits meant, I was left with a confused set of notes:

STEERING:

0 (all on): FAR LEFT (active low?)
8: mid left
12: 3/4 left
16+4 : 1/4 left
8+16 : mid left
4+8+16 : 1/4 left

32: FAR RIGHT
32 + 8 : MID RIGHT
32 + 4 + 8 : 3/4 right
32 + 16 + 8: MID RIGHT
32 + 16 + 8 + 4 : 1/4 right

32 + 16 : DEAD CENTER

In other words, I could see no pattern whatsoever.

So, I decided to look at the ROM program to see how it determined where the steering was positioned.  I found the relevant routine right here after a bit of searching:


When I saw this, I was quite confused because all it does is a loop that does an eXclusive-OR and right shift.  I decided to write a quick C program to replicate this algorithm and print out all of the possible results to see if I could see any pattern at all.

Here is the C equivalent algorithm I came up with:

unsigned char shifter(unsigned char u8)
{
        unsigned char u8A = 0;
        do
        {
                u8A ^= u8;
                u8 >>= 1;
        } while (u8 != 0);

        return u8A;
}

u8A represents 6809 register A (from the assembly language) and u8 represents the value stored on the stack.

The results of this program are here:

0 => 0 (00000000 => 00000000
1 => 1 (00000001 => 00000001
2 => 3 (00000010 => 00000011
3 => 2 (00000011 => 00000010
4 => 7 (00000100 => 00000111
5 => 6 (00000101 => 00000110
6 => 4 (00000110 => 00000100
7 => 5 (00000111 => 00000101
8 => f (00001000 => 00001111
9 => e (00001001 => 00001110
a => c (00001010 => 00001100
b => d (00001011 => 00001101
c => 8 (00001100 => 00001000
d => 9 (00001101 => 00001001
e => b (00001110 => 00001011
f => a (00001111 => 00001010
10 => 1f (00010000 => 00011111
11 => 1e (00010001 => 00011110
12 => 1c (00010010 => 00011100
13 => 1d (00010011 => 00011101
14 => 18 (00010100 => 00011000
15 => 19 (00010101 => 00011001
16 => 1b (00010110 => 00011011
17 => 1a (00010111 => 00011010
18 => 10 (00011000 => 00010000
19 => 11 (00011001 => 00010001
1a => 13 (00011010 => 00010011
1b => 12 (00011011 => 00010010
1c => 17 (00011100 => 00010111
1d => 16 (00011101 => 00010110
1e => 14 (00011110 => 00010100
1f => 15 (00011111 => 00010101
20 => 3f (00100000 => 00111111
21 => 3e (00100001 => 00111110
22 => 3c (00100010 => 00111100
23 => 3d (00100011 => 00111101
24 => 38 (00100100 => 00111000
25 => 39 (00100101 => 00111001
26 => 3b (00100110 => 00111011
27 => 3a (00100111 => 00111010
28 => 30 (00101000 => 00110000
29 => 31 (00101001 => 00110001
2a => 33 (00101010 => 00110011
2b => 32 (00101011 => 00110010
2c => 37 (00101100 => 00110111
2d => 36 (00101101 => 00110110
2e => 34 (00101110 => 00110100
2f => 35 (00101111 => 00110101
30 => 20 (00110000 => 00100000
31 => 21 (00110001 => 00100001
32 => 23 (00110010 => 00100011
33 => 22 (00110011 => 00100010
34 => 27 (00110100 => 00100111
35 => 26 (00110101 => 00100110
36 => 24 (00110110 => 00100100
37 => 25 (00110111 => 00100101
38 => 2f (00111000 => 00101111
39 => 2e (00111001 => 00101110
3a => 2c (00111010 => 00101100
3b => 2d (00111011 => 00101101
3c => 28 (00111100 => 00101000
3d => 29 (00111101 => 00101001
3e => 2b (00111110 => 00101011
3f => 2a (00111111 => 00101010

The value on the left are the steering bits.  The value on the right are what they get converted to by this algorithm.

After staring at this for a few minutes, I still could not see an obvious pattern.  Maybe some smart guy reading this will be able to figure out the relationship of the source and destination values and impress us all? :)

UPDATE: As the comments say, this is Gray Code.  The algorithm to convert the steering position back into Gray code is:

uint8_t u8GrayCode = (u8SteeringPosition >> 1) ^ u8SteeringPosition;

So for example, 0x3F is indeed (0x2A >> 1) ^ 0x2A.

At any rate, it occurred to me that I did not NEED to understand how the steering bits mapped to the steering mechanism.  All I had to understand was what the algorithm was.  And I had already found the algorithm in the assembly language.

So without understanding what the steering bits actually mean, I was able to implement the algorithm in Daphne and hook up my XBOX 360 wireless controller's analog stick to control the steering mechanism.

Here is a video of me carefully moving my analog joystick right to left:


My observation here is that while it's nice to understand exactly how everything about a game works, one does not need to understand everything in order to emulate it to some degree (although understanding things definitely improves the likelihood of accurate emulation).

2 comments:

  1. The input values are using a "gray code", where only one bit changes at a time, to avoid errors during transitions.

    http://en.wikipedia.org/wiki/Gray_code#Position_encoders

    ReplyDelete
  2. I was coming to post the same thing (Grey code), but you have beaten me to it :)

    ReplyDelete