Joystick Device Driver
======================

Steve Cormie (6 Nov 1991), Revision 0.01
----------------------------------------


Requirements
------------

1) The state of a joystick should be polled by a SWI call.
2) Must provide a consistent interface.
3) Software which requires the use of joysticks should not have to be modified
   in order to work on machines which have no built-in joystick ports but have
   been fitted with a podule and driving software from an ISV.
4) It should also be possible for ISVs to add extra joysticks to a product
   which already has built-in joystick ports (ideally the same podule and
   driving software described in 2).
5) There must be clear guidelines for ISVs who wish to produce drivers which
   adhere to the joystick interface.
6) Should be sufficiently fast.
7) Should it be possible to use the interface which will be defined on
   RISC OS 2 machines? - Could be very important to games developers.


Hardware
--------

It would be nice to be able to determine if the joystick hardware existed or
not.  This has not been implemented in hardware terms but the scheme which has
been proposed is as follows:

        - write &FF to a joystick port magic address
        - read back from the same address
        - if bit 0 of the byte read back is 0 then
                the hardware is present (bit 0 pulled low)
          else
                the hardware is not present (bus is pulled high)

The hardware latches the current state of a joystick AFTER a byte is read from
the magic address (this is to avoid reading while the lines are in a transient
state).  This means that the magic address must be read twice in order to get
the current state of a joystick, once to latch in the current state and then
again to read it.

The state byte which is returned has the following format:

        bits 7-0: XXXFRLDU

Where X - undefined
      U - Up
      D - Down
      L - Left
      R - Right
      F - Fire


Possible Interfaces to Joystick Device Driver
---------------------------------------------

There are three possible methods for implementing the joystick device driver.
These are described below with the advantages and disadvantages of each.


Simple SWI Interface
--------------------

This is the most simple approach where only an interface to the built-in joystick
ports is provided.  A module called Joystick would be written which would provide
the following SWI:

        SWI Joystick_Read
                In:     joystick number
                Out:    joystick state

On initialisation the module would test for the presence of the hardware and if
it was not there then the module would not initialise.  This allows ISVs to test
for the existance of built-in joysticks using RMEnsure and to load their own
driver if required.

Advantages:

a) Satisfies requirements 1.
b) Provides a consistent interface.  Software which uses joysticks simply calls
   the SWI (satisfies requirement 2).
c) ISVs can provide a module which replaces this SWI with one which will read
   the state of its own joystick devices (satisfies requirement 3).
d) Simple interface for ISVs to implement when writing their own joystick
   drivers (satisfies requirement 5).
e) Very fast (satisfies requirement 6).
f) Backwards compatible with RISC OS 2.  The same software can be run on RISC OS 2
   machines by loading a module which provides the Joystick_Read SWI (satisfies
   requirement 7).
g) Defines a standard interface which can be applied to RISC OS 2 and 3.
h) Quick and easy implementation (more time to do other work).
i) Small amount of additional documentation (one SWI).
j) Module would be very small (don't want to use too much ROM space).

Disadvantages:

a) It is not easy to satisfy requirement 4 ie. adding joysticks to a product
   which has built-in joystick ports.  ISV joystick drivers would replace the
   SWIs, effectively disabling the built-in joystick ports.  Device drivers
   which add extra joysticks would have to be different to those which merely
   provide the Joystick_Read SWI.  It could be argued that all we have to do
   is provide the primitive interface to the built-in joystick ports, any
   other issues such as extra joysticks can be handled by ISVs.  This means
   that software which uses more than two joysticks would have a different
   interface.


Vectored Interface
------------------

A new vector called PollV could be introduced.  Device drivers which handle
devices which can be polled hang a handler routine off this vector using OS_Claim.
The routines which are hung off this vector take the following parameters:

        device (eg. Joystick, UserPort etc.)
        device specific data

A module called Joystick would be written which would hang a joystick device
driver off PollV (only if the hardware is present).  The input to this routine
would be as follows:

        device (must be Joystick)
        joystick number

It's action would be:

        if device is not Joystick or joystick number > 1 then
                pass the call on to another handler
        else
                return the joystick state

ISV joystick device drivers could also hang handlers off PollV.  These routines
would respond to requests for joysticks 0 and 1 on machines with no built-in
joystick ports or could respond to requests for higher numbers for extra
joysticks and pass on requests for joysticks 0 and 1 to the above handler.

A SWI would be written which would poll a specified device ie:

        SWI OS_Poll
                In:     device (eg. Joystick)
                        device specific data
                Out:    state of device

This SWI would initiate a call which is passed down the list of handlers hanging
off PollV until one of them claims the call and responds with the state of its
device.  This SWI would be used to poll a joystick.

Advantages:

a) Requirement 1 is satisfied.
b) Provides a consistent interface.  Software which uses joysticks would
   simply call OS_Poll with the joystick number (satisfies requirement 2).
b) The OS_Poll SWI is all that software which uses joysticks need to get the
   state of built-in or extra joysticks.  ISV joystick device drivers would
   hang their handler off PollV and only respond to requests for the state
   of their joysticks.  This means that the Joystick module handler and the
   ISV handler can co-exist hence requirements 3 and 4 are met.
c) Satisfies requirement 6.
d) This mechanism will support any device which can be polled eg. external
   switches etc. all that changes is the device number and device specific
   data which is passed through OS_Poll.

Disadvantages:

a) There would be much more documentation required for this interface, the new
   vector and its uses, the OS_Poll SWI etc.
b) This would not be backwards compatible with RISC OS 2 (there is no PollV or
   OS_Poll SWI).

   To allow backwards compatibility SWI Joystick_Read could be added to the
   Joystick module.  This would call OS_Poll and some handler (possibly in
   an ISV driver) would return a state which Joystick_Read would then
   return.  On RISC OS 2 all that would be required is a module which
   provides the Joystick_Read SWI (however, this effectively reduces the
   interface back to the Joystick_Read SWI and all we have gained is the
   extra vector and the facility for ISVs to install extra joysticks).


Using DeviceFS
--------------

It could be argued that the correct approach for RISC OS 3 is to use DeviceFS.
A device would be created for each joystick eg. Devices:$.Joystick.0,
Devices:$.Joystick.1 etc.  To read the state of a joystick the software would
open the device using OS_Find and then read from it using OS_BGet until the
software terminates (at which point it must close the device).

Joystick device drivers provided by ISVs could then create their own devices
eg. Devices:$.Joystick.2 (as an extra joystick) or provide joystick devices
0 and 1 on machines which have no built-in joystick ports.

Advantages:

a) The state of a joystick can be polled by a SWI call (read the state using
   OS_BGet from the required joystick device, this satisfies requirement 1).
b) The DeviceFS module provides a standardised interface to device drivers
   within the RISC OS environment (this satisfies requirement 2).  Ideally, all
   device drivers written for RISC OS 3 should use this interface.
c) By adding joystick devices to Devices:$.Joystick. ISVs can replace or add
   whatever joysticks are required (this satisfies requirements 3 and 4).
d) The DeviceFS interfaces are already documented.  This reduces the amount
   of additional work which would have to go into documenting any new device
   driving algorithm (eg. implementing a polling vector). This satisfies
   requirement 5.
e) An unbuffered device driver has not been written for DeviceFS.  Development of
   this software will test that aspect of DeviceFS.
f) We demonstrate that DeviceFS is the correct way to implement device drivers.

Disadvantages:

a) It may not be as fast as the previously described methods (although it should
   still be sufficiently fast).
b) This is the method most incompatible with RISC OS 2 (DeviceFS does not exist).

   If this method were to be used I suspect that ISVs who write software that
   uses joysticks would create their own SWI interface in a seperate module.
   For RISC OS 3 this SWI would open the joystick devices and read from them.
   For RISC OS 2 a module could be written which provides the same SWI but this
   time it would have to read the state of a joystick directly (this would be
   hardware dependent).  This means that there will still be all manner of
   non-standard SWI interfaces which merely interface to the OS_Find and OS_BGet
   SWIs.

A more detailed description of the DeviceFS implementation follows, skip it if
there is too much detail.


Device Driver Under DeviceFS
----------------------------

The joystick device driver would take the form of a module called Joystick (as long
as this name has not been registered with us already).

Module workspace:

        - keep the following information for each device:
                - device driver handle (passed back from DeviceFS_Register, 0 if dead)
                - DeviceFS stream handle (passed in DeviceDriver_Entry Initialise)
                - internal stream handle (0 if not open)
                - address from which to read the joystick state

The module would do the following on initialisation:

        - allocate the module workspace
        - open the messages file
        - set device driver handle for both devices to 0
        - if <Joystick$Path> is not set then
                set Joystick$Path Devices:$.Joystick.
        - this allows joystick devices with names like Joystick:0

The module would respond to the following Services:

        Service_DeviceFSStarting
                - if hardware doesn't exist then
                        return
                - there must be a seperate device for each joystick so that ISVs can
                  provide additional joysticks (ie. can't use one joystick device with
                  special fields as the whole device would be replaced).
                - if the device driver handle for either device is non-0 then
                        error "Devices already registered"
                - register joystick 0 using DeviceFS_Register
                        r0 = 0 (character device, not full duplex)
                        r1 = pointer to the following
                                offset  0: offset to "Joystick.0"
                                        4: 0 (no path, unbuffered)
                                        8: 0 (no flags)
                                       12: 0 (no buffers so 0 size)
                                       16: 0 (read only so no transmit flags)
                                       20: 0 (read only so 0 size)
                        r2 = address of device driver entry point for joystick 0
                        r3 = 0 (private word)
                        r4 = workspace pointer
                        r5 = 0 (no special fields)
                        r6 = -1 (unlimited readers)
                        r7 = 0 (no writers)
                - store device driver handle for joystick 0
                - register joystick 1 using DeviceFS_Register
                        r0 = 0 (character device, not full duplex)
                        r1 = pointer to the following
                                offset  0: offset to "Joystick.1"
                                        4: 0 (no path, unbuffered)
                                        8: 0 (no flags)
                                       12: 0 (no buffers so 0 size)
                                       16: 0 (read only so no transmit flags)
                                       20: 0 (read only so 0 size)
                        r2 = address of device driver entry point for joystick 1
                        r3 = 0 (private word)
                        r4 = workspace pointer
                        r5 = 0 (no special fields)
                        r6 = -1 (unlimited readers)
                        r7 = 0 (no writers)
                - store device driver handle for joystick 1
                - set the following system variables
                        set Joystick$Device0 Joystick:0
                        set Joystick$Device1 Joystick:1
                - software which uses joysticks should read these system variables to
                  determine which joystick devices to use. 
                - device driver entry points merely set up a pointer to the appropriate
                  device information and then branch to a generic device handler.

        Service_DeviceFSDying
                - make sure that device driver handle for both devices is 0

        Service_DeviceDead
                - if device driver handle == 0 then
                        set handle for both drivers to 0
                  else
                        find device with specied handle
                        if found then set its handle to 0

The device driver for joystick 0 would do the following:

        - set pointer to device record for joystick 0
        - branch to generic device driver

The device driver for joystick 1 would do the following:

        - set pointer to device record for joystick 1
        - branch to generic device driver

The generic device driver would respond to the following calls:

        DeviceDriver_Entry 0 (Initialise)
                - if internal stream handle in device record != 0 then
                        error "Stream already exists"
                - store DeviceFS stream handle in the device record
                - create an internal stream handle
                - return internal handle
                - handle is not stored in the device record yet, that is done
                  by DeviceDriver_Entry 11 (Stream created)

        DeviceDriver_Entry 1 (Finalise)
                - set internal handle in device record to 0

        DeviceDriver_Entry 3 (Wake up for RX)
                - if internal handle in device record == 0 then
                        error "Device has not been opened"
                - read twice from the address in the device record to get state
                - call DeviceFS_ReceivedCharacter to pass back state
                        r0 = state
                        r1 = DeviceFS stream handle in device record

        DeviceDriver_Entry 11 (Stream created)
                - if internal stream handle in device record != 0 then
                        error "Stream already exists"
                - store the internal handle in the device record
                - the device record is now valid and state can be read from the
                  joystick device

The module would do the following when it dies:

        - deregister both joystick devices using DeviceFS_Deregister
        - close the messages file
        - free the module workspace

The module would have no SWIs of its own and would not use any vectors.


ISV Joystick Device Drivers Under DeviceFS
------------------------------------------

ISVs could produce drivers for their own joystick devices in the same manner
as above.  However, when their module received Service_DeviceFSStarting
they would look for Devices:Joystick to determine whether any joystick
devices had been registered already.  If they had then their module
would register its devices with numbers starting from the highest joystick
number + 1 (unless they wished to replace the built-in joysticks, in which
case the module would register devices 0 and 1 which would kill the original
devices).  The module would set Joystick$Device? system variables for
the joystick devices it created.


DeviceFS and Software Which Uses Joysticks
------------------------------------------

Any new software should use the joystick devices directly, ie. open the
required joystick device using OS_Find then read its state using OS_BGet
(then close the device when the software terminates).  This means that
the software always accesses joysticks in exactly the same way whether
they are built-in or added on and there is no requirement for options
to select the type of joystick.  The software can read the system
variables <Joystick$Device?> in order to get the devices to be used
for input.  In this way the user can specify which joystick to use in
an independent way.

Existing software which uses a SWI interface for joysticks will need a
module which replaces the appropriate SWIs.  The replacements should
read from the joystick devices in Devices:$.Joystick using the system
variables defined by the drivers eg. 

        SWI Read_Joy0
                if joystick 0 flag not set then
                        open "<Joystick$Device0>"
                        set joystick 0 flag
                read state from device and return it


Question
--------

Should the interface we provide be easy to simulate on RISC OS 2 machines?

   For the vector and DeviceFS methods this would not be easy.  I suspect that
   what software developers will do if we used one of these two methods is
   to write a module which provides a simple SWI interface which hides the
   real mechanism.  This is a strong argument for just providing a simple
   SWI interface ourselves which could then be adopted as a standard interface
   for both RISC OS 3 and RISC OS 2.  The only drawback with the simple SWI is
   that ISVs would have to create a different interface for software which
   uses more than two joysticks.



Recommendations
---------------

If the answer to the above question is no (or it doesn't really matter) then
we should go with DeviceFS.  After all, we have produced DeviceFS in order to
specify a standard interface to devices.  It allows ISVs who produce add-on
joysticks to add them in a clearly defined way (through DeviceFS) and software
which uses joysticks can access built-in or ISV supplied devices in exactly
the same way.

If the answer to the above question is yes then the simple SWI interface is
the most suitable.  I suspect that this would also be the most popular choice
with external developers (hence the choice which would probably make us
the most money).
