
            Modified Modbus protocol over MIDI Sysex

Base message framing is an RSD Sysex very similar to an MTS-232 message.
Within the payload of this framing is a Modbus/TCP message in mostly Ascii
representation.  The ascii representation is used to get around the
inherent 7-bit problem of midi.  To save some space on Modbus/TCP message
overhead, the lead bytes are represented as 7-bit versions of the actual
8-bit values.  This isn't expected to cause any problems.

Only a subset of the Modbus messages are implemented.  This subset does
NOT correspond to any of the reccomended sets in the Modbus/TCP document!

MTS-232 message format (general, 'data' type message):

		F0  00 00 40  00  dd  ss  (7x 7x 7x)  F7
		 0   1  2  3   4   5   6    7  8  9   10

		00 00 40	= RSD manuf ID
		00			= MTS-232 device class
		dd			= MTS-232 box address, 00..127 (set with jumpers)
		ss			= setup byte
		7x			= optional data nibbles, which must be in pairs

We use the format up through byte 5, then begin with the Modbus/TCP header:

        xx  xx  pp  pp  ll  ll  id  message

        xx xx       = transaction id, usually 00 00
        pp pp       = protocol identifier, always 00 00
        ll ll       = length in bytes of following data, <= 256
        id          = device identifier

Some parts of the standard Modbus/TCP header above are useless, since the
data already exists in another part of the message or is useless.  We will
therefore preform a perversion/translation on the header by deleting the
protocol identifier and using the "dd" device number from the MTS-232 header
in place of the "id" byte.  We will also drop the length, since the message
length will be obvious from the message code, and can be checked against the
F7 terminator.

==============================================================================
==============================================================================

This gives us a combined fixed header of:

        F0 00 00 40 04 dd rr tt tt message  F7
         0  1  2  3  4  5  6  7  8 9

        F0          = Sysex start
        00 00 40    = RSD Manuf Id.
        04          = Modbus Command device class
        dd          = Modbus PLC box address, 0..127
        rr          = Sender's ID, will be used in Response messages
        tt tt       = arbitrary transaction id echoed to response message

Once past this header we have a standard Modbus-Ascii message representation.
The Ascii representation is simply to take the two hex digits of each byte
and represent them in 0..F hex ascii characters.  (The spec doesn't indicate
if we should use upper or lower case A..F, so we will use upper.)

Message formats are dependent on opcode.  However, in general the opcode is
one byte, addresses are 2 bytes (16 bits), and data is in either 8 or 16 bit
framing units.  In 16 bit units, the data is represented in big-endian form.
In 8 bit units, the data is essentially little-endian if more than 8 bits
are represented.

==============================================================================
==============================================================================

Modbus responses are identical to Modbus commands, except for the device
class byte:

        F0 00 00 40 05 dd rr tt tt message  F7
         0  1  2  3  4  5  6  7  8 9

        F0          = Sysex start
        00 00 40    = RSD Manuf Id.
        05          = Modbus Response device class
        dd          = Command sender's ID
        rr          = Sending Modbus PLC box address, 0..127
        tt tt       = arbitrary transaction id echoed from command message

Once past this header we have a standard Modbus-Ascii message representation.
The Ascii representation is simply to take the two hex digits of each byte
and represent them in 0..F hex ascii characters.  (The spec doesn't indicate
if we should use upper or lower case A..F, so we will use upper.) nb. Actually
it does specify A..F rather than a..f so we guessed right.

Message formats are dependent on opcode.  However, in general the opcode is
one byte, addresses are 2 bytes (16 bits), and data is in either 8 or 16 bit
framing units.  In 16 bit units, the data is represented in big-endian form.
In 8 bit units, the data is essentially little-endian if more than 8 bits
are represented.

==============================================================================
==============================================================================

Supported Modbus Opcodes:

        01      Read Coil Status
                Reads zero-relative bit array of output latches.

        02      Read Input Status
                Reads zero-relative bit array of input values.

        03      Read Holding Registers
                Reads array of V-memory holding registers.
                The input and output bit arrays are mapped into this space
                somewhere, so the inputs and outputs can be read in 16 bit
                words.  We need to define the shape of this mapping.

        04      Read Input Registers
                Reads zero-relative array of 16 bit words overlaying the
                input bit array accessed by function 02.

        05      Force Single Coil
                Sets a single bit in the zero-relative output bit array.

        06      Preset Single Register
                Sets a 16 bit quantity in mapped V-memory.
                We need to define the mapping.

        0F      Force Multiple Coils
                Set an array of bits in the zero-relative output bit array.

        10      Preset Multiple Registers
                Set an array of 16 bit quantities in V memory.
                We need to define the mapping of ins and outs to this space.


==============================================================================
==============================================================================

Command details:

    Read Coil Status

        Request:

            01  aaaa llll

            Read one or more output register bits.  The LOWEST numbered
            output bit is aaaa.  The number of bits is llll.  Both numbers
            are BIG-endian.  The maximum length is 2000 bits.

            A read that begins in a valid range and extends past the last valid
            coil will return zeros for the nonexistant coils.

            A read that starts past the last valid coil will return an error.

        Response:

            01 ll dd....

            ll = byte length of dd... in HEX DIGITS.  Multiply by 2 for Ascii
            data length.

            dd  = returned coil data.  The lowest numbered coil is in bit 0x01
            of the first byte,  Coil 8 is bit 0x01 of the second byte, etc.

        If an error occurs no response is returned, instead there was an
        MMRESULT error code on the write of the request data.  The response
        is of course returned on the midi read port for the device.


    Read Input Status

        Request:

            02  aaaa llll

            Read one or more input register bits.  The LOWEST numbered
            input bit is aaaa.  The number of bits is llll.  Both numbers
            are BIG-endian.  The maximum length is 2000 bits.

            A read that begins in a valid range and extends past the last valid
            input will return zeros for the nonexistant coils.

            A read that starts past the last valid input will return an error.

        Response:

            02 ll dd....

            ll = byte length of dd... in HEX DIGITS.  Multiply by 2 for Ascii
            data length.

            dd  = returned input data.  The lowest numbered input is in bit 
            0x01 of the first byte,  Coil 8 is bit 0x01 of the second byte, etc.

        If an error occurs no response is returned, instead there was an
        MMRESULT error code on the write of the request data.  The response
        is of course returned on the midi read port for the device.


    Read Holding Registers

        Request:

            03  aaaa llll

            Read one or more 16-bit quantities from V-memory.  V-memory
            includes both the input and output areas as well as any other
            ram-like functions that might be present in the device.

            In general each input or output module is mapped to a single
            16-bit word.  If the module has more than 16 points it is mapped
            to multiple contiguous words.  Modules containing both input and
            output will be mapped disjointly into the input and output areas
            of V-memory.

        Response:

            03 ll dddd...

            ll = byte length of dddd.... in HEX DIGITS.  Multiply by 2 for
            Ascii data length.

            dddd = returned data as 16 bit BIG ENDIAN value for first (lowest
            addressed) item, followed by dddd items for sequentially higher
            addresses.

        If an error occurs no response is returned, instead there was an
        MMRESULT error code on the write of the request data.  The response
        is of course returned on the midi read port for the device.


    Read Input Registers

        Same format and semantics as Read Holding Registers, except that the
        data is limited to the input values and they start at relative
        address zero in the request.


    Read Input Registers

        Same format and semantics as Read Holding Registers, except that the
        data is limited to the input values and they start at relative
        address zero in the request.

         Request:

            04  aaaa llll

            Read one or more 16-bit quantities from input memory.

            In general each input or output module is mapped to a single
            16-bit word.  If the module has more than 16 points it is mapped
            to multiple contiguous words.  Modules containing both input and
            output will be mapped disjointly into the input and output areas.

        Response:

            03 ll dddd...

            ll = byte length of dddd.... in HEX DIGITS.  Multiply by 2 for
            Ascii data length.

            dddd = returned data as 16 bit BIG ENDIAN value for first (lowest
            addressed) item, followed by dddd items for sequentially higher
            addresses.

        If an error occurs no response is returned, instead there was an
        MMRESULT error code on the write of the request data.  The response
        is of course returned on the midi read port for the device.


    Force Single Coil

        Request:

            05  aaaa vv 00

            Set the single output bit indexed (from zero) by aaaa to the
            value in vv.  If vv == FF the bit is turned on, else if vv == 00
            the bit is turned off.  Any other value is an error and rejected.

        Response:

            Other than the MMSTATUS value there is no response.


    Preset Single Register

        Request:

            06  aaaa dddd

            Using V-memory mapping stuff the 16 bit value dddd into the word
            at address aaaa.  If the word is really smaller than 16 bits some
            high-order bits could be lost.

        Response:

            Other than the MMSTATUS value there is no response.


    Force Multiple Coils

        Request:

            0F aaaa cccc ll dd....

            Set an array of output bits starting at the LOWEST numbered bit
            aaaa.  The number of bits to set is in cccc, big endian.  The
            length of the data byes in HEX BYTES is ll.  Multiply by 2 to
            get Ascii bytes.  The actual data values are in the dd... bytes
            following.

            The first 8 or fewer coils are represented in the first dd byte.
            The lowest numbered coil is in bit 0x01.  If fewer than 8 coils
            are being set, the high-order bits are ignored and should be zero.
            If more than 8 coils are being set, the next 8 are in the following
            byte, and so on.

        Response:

            Other than the MMSTATUS value there is no response.


    Preset Multiple Registers

        Request:

            10 aaaa cccc ll dddd....

            Set an array of V-memory words beginning at the LOWEST numbered
            word aaaa.  The number of words to be set is cccc, max = 100.

            ll = the length of the data in HEX BYTES.  This should be twice
            the cccc value.  Multiply this by 2 to get the number of Ascii
            bytes.

            dddd = the first word of data in BIG ENDIAN format.

        Response:

            Other than the MMSTATUS value there is no response.


==============================================================================
==============================================================================

                    Modbus V-Memory mapping for EBC and ECOM

The following opcodes talk to "type 4" Modbus memory.  This is implicitly
similar to V-memory on an AutomationDirect PLC.  It contains the input points,
output points, counters, other special functions, and any actual variable RAM.

The following mapping is designed for consistency with the 205 series PLC
engines, which use a very similar mapping.


             PLC                      CCM                    Modbus
============================    ===============     ========================
Type    Base O  Base V   Ct     Type     Base H     Type    Base D  Commands
        (octal) (octal) (Dec)   (hex)    (hex)      (..)    (dec)    (hex)
====    ======  ======  ====    ====     ======     ====    ======  ========
 GX       GX0           2048     32        001       1x         0   02
 X         X0    40400   512     32        101       1x      2048   02
 X         X0    40400   512     31       4101       4x     16640   03 06 10                                                    
 SP       SP0    41200   512     32        181       1x      3072   02
 SP       SP0    41200   512     31       4281       4x     17024   03 06 10

 GY       GY0           2048     33        001       0x         0   01 05 0F 
 Y         Y0    40500   512     33        101       0x      2048   01 05 0F
 Y         Y0    40500   512     31       4141       4x     16704   03 06 10
 C         C0    40600  1024     33        181       0x      3072   01 05 0F
 C         C0    40600  1024     31       4181       4x     16992   03 06 10
 S         S0    41000  1024     33        281       0x      5120   01 05 0F
 S         S0    41000  1024     31       4201       4x     16896   03 06 10
 T         T0    41100   256     33        301       0x      6144   01 05 0F
 T         T0    41100   256     31       4241       4x     16960   03 06 10
 CT       CT0    41140   128     33        321       0x      6400   01 05 0F
 CT       CT0    41140   128     31       4261       4x     16992   03 06 10

 TV        V0        0   256     --        ---       3x         0   04
 TV        V0        0   256     31        001       4x         0   03 06 10
 CV     V1000     1000   128     --        ---       3x       512   04
 CV     V1000     1000   128     31        201       4x       512   03 06 10

 V      V1400     1400  3072     31        301       4x       768   03 06 10
 V     V10000    10000  4096     31       1001       4x      4096   03 06 10

 L                    131072     37                  --
 Z                     65536     36                  --


        Inputs  (Modbus 1x, CCM 32)
    ========================================
    GX      Global I/O (Inputs)
    X       Inputs
    SP      Special Purpose Relays (flags)

        Outputs (Modbus 0x, CCM 33)
    ========================================
    GY      Global I/O (Outputs)
    Y       Outputs
    C       Control Relays
    S       Stage Status Bits
    T       Timer Status Bits
    CT      Counter Status Bits

        Timer-Counter values (Modbus 3x, CCM 31)
    ========================================
    TV      Timer Current Values
    CV      Counter Current Values

        Data Memory (Modbus 4x, CCM 31)
    ========================================
    V       V Memory

        Special Memory
    ========================================
    L       Ladder Program
    Z       Scratchpad RAM


Inputs and outputs both have an offset of 2048 decimal or 0x800 hex.

There does not appear to be a way to get to analog data except the read
multiple and write multiple commands.

The Output area (0x) also has a number of special-purpose latches such as
control relays, timer and counter status bits, and stage bits.  These
exist at higher offsets than the normal I/O pins.

V-memory can hold constants, or for some I/O modules it will also hold
auxilliary I/O data.


==============================================================================
==============================================================================
             PLC                      CCM                    Modbus
============================    ===============     ========================
Type    Base O  Base V   Ct     Type     Base H     Type    Base D  Commands
        (octal) (octal) (Dec)   (hex)    (hex)      (..)    (dec)    (hex)
====    ======  ======  ====    ====     ======     ====    ======  ========
 GY       GY0           2048     33        001       0x         0   01 05 0F
 Y         Y0    40500   512     33        101       0x      2048   01 05 0F
 C         C0    40600  1024     33        181       0x      3072   01 05 0F
 S         S0    41000  1024     33        281       0x      5120   01 05 0F
 T         T0    41100   256     33        301       0x      6144   01 05 0F
 CT       CT0    41140   128     33        321       0x      6400   01 05 0F

 GX       GX0           2048     32        001       1x         0   02
 X         X0    40400   512     32        101       1x      2048   02
 SP       SP0    41200   512     32        181       1x      3072   02

 TV        V0        0   256     31        001       4x         0   03 06 10
 CV     V1000     1000   128     31        201       4x       512   03 06 10
 V      V1400     1400  3072     31        301       4x       768   03 06 10
 V     V10000    10000  4096     31       1001       4x      4096   03 06 10
 X         X0    40400   512     31       4101       4x     16640   03 06 10
 SP       SP0    41200   512     31       4281       4x     17024   03 06 10
 Y         Y0    40500   512     31       4141       4x     16704   03 06 10
 C         C0    40600  1024     31       4181       4x     16992   03 06 10
 S         S0    41000  1024     31       4201       4x     16896   03 06 10
 T         T0    41100   256     31       4241       4x     16960   03 06 10
 CT       CT0    41140   128     31       4261       4x     16992   03 06 10

 TV        V0        0   256     --        ---       3x         0   04
 CV     V1000     1000   128     --        ---       3x       512   04
==============================================================================
==============================================================================

PLC        CCM                    Modbus
====  ===============     ===============================
Type  Type     Base H     Type    Base D Base H  Commands
      (hex)    (hex)      (..)    (dec)  (hex)    (hex)
====  ====     ======     ====    ====== ======  ========
 GY    33        001       0x         0    000   01 05 0F 
 Y     33        101       0x      2048    100   01 05 0F
 C     33        181       0x      3072    180   01 05 0F
 S     33        281       0x      5120    280   01 05 0F
 T     33        301       0x      6144    300   01 05 0F
 CT    33        321       0x      6400    320   01 05 0F


 GX    32        001       1x         0    000   02
 X     32        101       1x      2048    100   02
 SP    32        181       1x      3072    180   02


*TV    --        ---       3x         0    ---   04
 TV    31        001       4x         0    000   03 06 10
*CV    --        ---       3x       512    ---   04
 CV    31        201       4x       512    200   03 06 10
 V     31        301       4x       768    300   03 06 10
 V     31       1001       4x      4096   1000   03 06 10
 X     31       4101       4x     16640   4100   03 06 10
 SP    31       4281       4x     17024   4280   03 06 10
 Y     31       4141       4x     16704   4140   03 06 10
 C     31       4181       4x     16992   4180   03 06 10
 S     31       4201       4x     16896   4200   03 06 10
 T     31       4241       4x     16960   4240   03 06 10
 CT    31       4261       4x     16992   4260   03 06 10

* - remaps to following command.... Somehow

Base is in bits for ModBus type 0 and 1.
Base is in words (!) for ModBus type 3 and 4.
Length is in bytes for all size values. So for
ModBus type 3 and 4 commands multiply address range
by 2 to get legal sizes.... Translate to CCM values
by adding one to odd sizes.

V-Memory addresses are 16 bit values.
