The result of tonight's frivolities: a fully-functional USB adapter for the original Gravis GamePad Pro in GrIP mode, which enables all ten buttons and D-pad input to work over a standard 15-pin gameport.

This on its own is not special - at least two other projects out there replicate this exact functionality - but the key here is that *I understand how this works now*, and I can start work on supporting all the other #GravisGrIP controllers. Stay tuned!

#retrocomputing #retrogaming #arduino

The subject of tonight's frivolous nonsense is the second of five #GravisGrIP devices I'd like to support with my adapter. The BlackHawk Digital was the first Gravis joystick to support the GrIP protocol, and - without the fall-back analog gameport mode of the GamePad Pro or forwards-compatibility with USB - is basically lost technology at this point. This week - tonight, with any luck - I intend to change that.

With three analog axes, this will be tougher to discombobulate than the GPP.

Progress on reverse-engineering the Gravis BlackHawk Digital is going okay. Using what's known about the GamePad Pro, I've made enough educated guesses to mostly understand the digital inputs and the throttle wheel (which seems to be a 6-bit analog input, not 8-bit as I'd assumed), but moving the actual joystick changes enough about the data packet that my program wigs out and can't tell what's what anymore, and that's where my guesses start going totally wrong.

The GamePad Pro's #GravisGrIP protocol is well-understood, and totally documented in the last paragraph in this archived project: https://github.com/prosper00/GRiP-duino

However, the same thing doesn't seem to exist for the BHD or other devices.

All but the GrIP MultiPort are supported by Linux, but the code here is totally opaque to me: https://github.com/ilbers/linux/blob/master/drivers/input/joystick/grip.c

I've cheekily reached out to Vojtech Pavlik, that driver's author from 24 years ago, to see if he's kept any notes - we'll see how lucky I am there.

GitHub - prosper00/GRiP-duino: Arduino project to convert 15-pin Gravis Gamepad Pro gamepads to USB-HID in digital GRiP mode (supporting all buttons)

Arduino project to convert 15-pin Gravis Gamepad Pro gamepads to USB-HID in digital GRiP mode (supporting all buttons) - prosper00/GRiP-duino

GitHub

I did find a Linux driver for the MultiPort, something Mr. Pavlik didn't end up writing. I've found it in the input drivers for Android Wear, of all things, but it seems to be what I think it is: https://android.googlesource.com/kernel/msm/+/android-wear-5.1.1_r0.2/drivers/input/joystick/grip_mp.c

That, terrifyingly, suggests the GrIP MultiPort has two-way comms (rather than just it reporting stuff to the PC like the gamepads or joysticks would), so implementing support for it in my adapter could be a difficult task.

drivers/input/joystick/grip_mp.c - kernel/msm - Git at Google

Progress! The first three lines here are with the joystick near-centre, the second three are where my program wigged out and it's because there's another one or more start bits to the packet. So possibly some bits near the start-part are also used to communicate the joystick's angle, or there's multiple different packets this thing sends to communicate different things. I don't love either possibility, so more work is required to figure out what's going on.

Progress today on reverse-engineering the #GravisGrIP BlackHawk Digital joystick - there are indeed two different packets this thing sends! Bit 9 is a zero if it's updating digital inputs, and a one if it's changing joystick inputs.

This was confusing because the GamePad Pro's start sequence was always a 0, a number of 1s and another 0; I'd assumed that trailing zero was part of the start bits, but the GPP has no analog controls, so even if that's in the GrIP spec it'd never show a one there.

Remaining mysteries:
- Why is everything (apparently) repeated? Is this just an artefact of the BlackHawk not fully implementing everything in GrIP? Breaking down a packet from one of the Xterminator controllers might answer this.
- How do I actually read the joystick inputs, and why are there two distinct sections with 12 and 16 bits?
- Why does the throttle control mess with the POV inputs, and how do I tell those apart?
The reason I think it's just duplicating to fill out the GrIP packet is that the Linux GrIP driver has two main functions, "grip_gpp_read_packet" and "grip_xt_read_packet", despite supporting four different devices - I think the GamePad Pro has a GrIP protocol subset all to itself, and the other three (BlackHawk Digital, Xterminator gamepad, Xterminator Dual Control joystick) are all a second "XT" subset. If true, that should actually mean I understand enough to try reading from my XDC now.

For reference, this is the Linux driver for Gravis GrIP devices: https://github.com/ilbers/linux/blob/master/drivers/input/joystick/grip.c

My programming skills are pretty simple, so this is an impenetrable wall of optimisation and bit-shifting to me, and I have no hope of reverse-engineering anything substantial out of here.

linux/drivers/input/joystick/grip.c at master ยท ilbers/linux

Copy of https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git - ilbers/linux

GitHub
My program now discerns between analog and digital update packets, and visually formats them like this, so I can click and wiggle stuff to visually see what's going on in the packet. I have a second function I can swap in that just dumps the packet with tab separation so I can easily copy-paste it into Excel for deeper analysis.

Just discovered someone has actually done the hard work and ported the XT portion of the Linux GrIP driver to Arduino: https://gist.github.com/Hyratel/4e689925743fb789b5cf3cca3812511a

Took me ages to stumble on it since it doesn't actually mention any of the devices, and it's still a complex program, but there's more clues for me to work from here.

GrIP-XT-serial.ino

GitHub Gist: instantly share code, notes, and snippets.

Gist

There's a big difference between my code and anything approaching "official" - the Linux driver has CRC checking and all sorts of super-efficiency, whereas I'm just banging two packets' worth of bits out of the gameport, picking one complete packet out of that, and joysticking as needed.

I don't want to be a protocol-completionist here - I just want to make a thing you plug a joystick into and it works well enough to play some games with it, even if I'm not catching every single packet.

@timixretroplays nop comment to make a backreference for you
@timixretroplays for thoroughness onto this thread: that leading 1 you're seeing on the packet is part of the 2-bit Identifier front of the packet. it sends 4 packets of Length 20, with 2 address bits on the front, 14 bits of Data, and 4 bits of CRC on the back
Axes are all 6-bit. X and Y are in p1, throttle and hat in p3, buttons in p4. p2 appears to be unused on the BHD

@hyratel right, thanks - this is starting to make sense. The Xterminator joystick adds an analog thumb-stick and the gamepad has two analog rear buttons, I guess that's what P2 is for.

CRC would explain why multiple bits change where I expected to see only one...

Where's the true start of the packet? How does your code find/sync to it? With the answer to that I should have all the information I need to understand these controllers.

@timixretroplays Clock holds low and there's 2 pulses on Data, and then Clock starts flashing. you should be able to see this in my Gist code, following along with the annotated version on the AVRfreaks forum
@hyratel oh, okay. My code wouldn't notice that at all - all I'm doing so far is interrupting when the clock falls and reading data. I'll play around with that. Thanks so much for your help with this!