What’s new?
- I have improved the I/O podule ADC emulation so that ranges can be specified. This allows, for example, a single axis to be used as an accelerator and brake in Saloon Cars Deluxe. It may also allow for an effective deadzone to be specified, but I have not had a chance to try this.
- The format of the ADC mapping *commands have changed. See the 'MapSCD' file in the src.utils directory for an example.
- I have also had to re-introduce a couple of floats in the scaling. However, they are not in any SWI handlers, so there should be no regression to the abort situation.
- The mouse pointer and buttons can be controlled with a joystick
- Resolved a bug causing a crash when reading the manufacturer and product strings
Download
Version 0.20 is attached to this post.
A slightly earlier version is on GitHub for convenient browsing. I intend to sort this out properly at some point, so it can become the master repository. I will keep an eye out on how ROOL and others use Git. Link here: https://github.com/riscos-richard/USBJoystick
Requirements
You need a RISC OS 5 machine with USB hardware. I have only tried a Pi model B+. In theory, any USB HID-compliant joystick will work, but again, I have only tried what I have:
- a USB-Atari interface, with an old Atari joystick, and a Chinese knock-off MegaDrive pad
- a wireless XBOX360-style controller
https://github.com/philpem/LineEditor
Please make sure you don’t have any unsaved data before trying this module!
SparkFS (or similar) will be needed to open the attached zip file.
Starting
The zip file contains a single RISC OS module, USBJoystick. Just double-click it. The 'src' directory may interest you if you have the ROOL DDE.
Aso in the 'src' directory is a 'utils' directory, which contains a couple of BASIC test applications:
- ADCTest: tests the I/O module ADC OSByte interface)
- AcornTest: tests the Acorn 8-bit and 16-bit Joystick_Read SWI
- RTFMTest: tests the RTFM interface - this is not currently usable
- SerPortTest: tests the Serial Port (Vertical Twist) Joystick_Status SWI
Joystick discovery/enumeration
Open a Task Window (CTRL-F12) and type '*USBJoystick_List’. You should see some information on whatever joystick(s) you have plugged in. Each joystick is given an id number, starting from zero.
If you don’t see your joystick, then USBJoystick has not been able to detect it (can you see it if you use the OS command, '*USBDevices’?).
Enumeration
Joysticks will be discovered when the module starts. If you connect a joystick later on, it should be automatically detected (without restarting the module). Similarly, if you remove a joystick, the module will stop talking to it.
Mapping
The classic Acorn and Serial Port Joystick APIs refer to joysticks by a joystick number (0 or 1, for player 1 and 2 respectively). Note that the RTFM and Serial Port APIs only support one or two joysticks, but the Acorn API can support many more.
Each joystick number can offer two directional axes, typically up/down (Y) and left/right (X). A modern USB joystick will have multiple analogue axes (X, Y, Z, RX, RY, RZ) and separate digital inputs (DPAD or HAT switches). The desired USB axes need to be 'mapped' to the legacy number, X axis and Y axis.
As of version 0.07, you can map up to 8 buttons, but these will only work independently with the Acorn Joystick APIs. Games using the Serial Port or RTFM API will see a 'fire' when any of the buttons are pressed.
You can see the mapping with *USBJoystick_Mapped.
Automatic mapping
As of version 0.08, USBJoystick tries to automatically map the USB stick, axes and buttons across to the legacy items. The stick numbers will be assigned based on the order the USB devices are identified. If your joystick has a DPAD or HAT switch they will be mapped to the Serial Port, RTFM and Acorn 8-bit X and Y axes. If you have analogue X and Y axes, they will be mapped to the Acorn 16-bit X and Y axes. The buttons will be mapped in their order they the controller reports them - usually the first one reported is the primary.
Manual mapping
If the automatic mapping is not to your satisfaction, it can be adjusted with a number of *commands: *USBJoystick_MapStick, *USBJoystick_MapAxes8, *USBJoystick_MapAxes16 and *USBJoystick_MapButtons. Use the help to discover the exact syntax, e.g. *Help USBJoystick_MapStick will show you how to use the stick mapping feature.
You need to test your joystick to determine which axes and button you want to use. Type 'USBJoystick_Read 0' (or whatever joystick id you want). You should see some raw data showing the state of each axis and button. If you don’t touch the joystick, they will all be at their rest positions. Move the joystick up, and whilst holding that position, enter the read command again. Compare the two read outputs: you should be able to spot the value changing on one of the axes. This will be the Y-axis. Make a note of the axis number.
Repeat the above for identifying the X-axis, and each button. At the end of the process, you should know for each joystick id, the x and y axes numbers, and the primary button number. To map these up to the old Acorn/SerialPort API, you need to use the commands mentioned at the beginning of this section.
If any of your axes are working the wrong way around (e.g. up is down and down is up) then you can use the *USBJoystick_Flip command to invert the direction.
Please note that the mapping is not saved anywhere - it is just remembered in RAM. If you unplug the joystick, kill the USBJoystick module, or reboot your machine, the mapping will be lost. Once you have it figured out, you may wish to create an obey file which will set it up again for you.
Acorn I/O Podule Analogue to Digital converter
USBJoystick can emulate the ADC converter found on the I/O podule. It hooks into OSByte 128, so software using this SWI (including BASIC’s ADVAL function) will work. You can map an axis, or part of an axis, to an ADC channel, and a pair of buttons for fire. Please investigate the *command help: there is some mapping required.
The partial mapping is useful for configurations like a single Y axis (0 for middle, +32768 for top, and -32767 for bottom), where you want to map ADC Channel 0 to ‘up’ (so 0 to 32768 will be translated to 0 to 65520) and Channel 1 to ‘down’ (so 0 to -32767 will be translated to 0 to 65520).
Using the Joystick
Once you have mapped, you can use any software which supports the Acorn (SWI Joystick_Read) or Serial Port (SWI Joystick_Status). The USB Joystick driver module will transform the USB data into a format for those SWIs, and respond to their requests. With the help of ADFFS, games which use the RTFM API should also function.
I have tried some of the games from this web site: James Pond and SWIV work over the Serial Port SWI, and Zool works over the Acorn SWI.
Testing
Obviously, this thing is far from finished. It clearly needs more testing: one machine, two USB joysticks, and three games doesn’t really cut it!
All feedback is welcome.
Technical matters
If you have any problems with the module, it would be helpful if you could run Reporter before you run the module. If Reporter is active, some debugging guff will appear in Reporter’s log. This may help me! Reporter is available here: http://www.avisoft.f9.co.uk/Reporter.htm
The *command *USBJoystick_Debug 0 (replacing 0 for your joystick id, or removing it entirely to see all) may also provide useful information for me.
The output from Colin Granville's USBDescriptors output may be useful.
Acknowledgements
Jon’s efforts with JASPP itself was my inspiration for developing this (obviously, this is tiny effort in comparison!). Also, many thanks to the following people over on the ROOL forums who offered advice and put up with my silly questions: Jeffrey Lee, Rick Murray, Stuart Swales, Colin Granville, Simon Birtwhistle, and Chris Mahoney. Thanks to James Peacock for EtherUSB, and Colin Granville for USBDescriptors. The code uses components from NetBSD and XMAME. There are further notes in the source code.
Todo: Refactoring
Remove floats so we don’t need FPADONE IN 0.12
- Listen for service calls from USBDriver Starting/Dying
- Do we really need joy_index to be a global?
Implement releasing of memory in clean_joystick_data_item()DONE IN 0.10
- Determine correct error-handling strategy for module_finalise()
Todo: Features
Support multiple buttons in the mappingDONE IN 0.07
- Support SerialPort 'stir', 'flag A' and 'flag B' - extra buttons/mappings?
Decide if we need ability to reverse an axis in the mappingDONE IN 0.08
Emulate 16-bit Acorn Joystick_Read responseDONE IN 0.7
Emulate RTFM joystick API (based on Econet hardware peeks)DONE IN 0.06 (with ADFFS)
Emulate Acorn I/O podule API (ADVAL and associated OSBYTEs)DONE IN 0.12
- Save settings into Choices$Write and read back on init
- Load settings via USB alias system (used by !Printers). Would it be useful?
Consider auto-assigning legacy joystick numbers as devices are discoveredDONE IN 0.07
Allow Joystick actions to be mapped to keypresses or mouse movesDONE IN 0.20
- Formalise an SWI interface for device enumeration and configuration. This would allow a Configure plug-in to be developed.