c0nb0xDeveloper's info

Source Code (v1.01)

c0nb0x_101_src.zip (118 KB)

The package includes a .cbp (Code::Blocks project) with windows and linux build targets.
allegro5.0.10 (or newer) is required, and the project build settings may need adjustments to get the include/libs dirs right.


c0nb0x & x0xb0x Serial Protocol

To communicate with the x0xb0x, c0nb0x uses the same protocol which is used originally in c0ntr0l.
However, c0nb0x introduces some additional messages on top of that..

Everything between the app and x0x is sent in the form of packets:

[msgID] [size1] [size2] <data> [CRC]
  • msgID is the message ID

  • size1 and size2 are used together to form a uint16_t data_size for the data in the packet

  • data is a variable-sized chunk of bytes, if any

  • CRC is always the last byte, it is calculated by including the bytes from msgID all the way to the data section (if any)

The smallest possible packet (one which has no data) is 4 bytes long.
The packet total size is thus (4+data_size).

Note

While it’s technically possible to have a very big packet (since the data_size is a uint16_t type) i would strongly recommend to never use packets with more than 60 bytes of data, since the firmware accumulates the data for a packet into a buffer which is defined as 64 bytes long (see the definition of UART_BUFF_SIZE in compcontrol.c in the firmware).

In order to not break compatibility with the stock firmware - keep the size1 byte zero always, and don’t put more than 60 bytes into one packet (4+60=64).

This means that we’re stuck to having a useless byte in all packets, sadly it cannot be changed, since it will break compatibility with the existing x0x OSes.
It’s not possible to even use this byte for other purposes (like to extend it as msgID2 or something else) since the firmware does use it to calculate the size and any nonzero value in that byte will be interpreted as a very big packet.

Messages

msgID DESCRIPTION USED Direction
x0x<->APP
NAME DEC HEX
MSG_UNUSED 0 00 ? ?
MSG_PING 1 01 Ping pong? Y <->
MSG_PATT_WR 16 10 App sends a pattern to the x0x Y <-
MSG_PATT_RD 17 11 App requests a pattern from the x0x Y <-
MSG_PATT_LOAD 18 12 App wants x0x to select pattern X? - <-
MSG_PATT_GET 19 13 App wants to know the pattern slot? - <-
MSG_PATT_PLAY 20 14 - <-
MSG_PATT_STOP 21 15 - <-
MSG_PATT 25 19 x0x sends a pattern to the App Y ->
MSG_TRACK_WR 32 20 App sends a track to the x0x? - <-
MSG_TRACK_RD 33 21 App requests a track from the x0x? - <-
MSG_TRACK_LOAD 34 22 App wants x0x to select track X? - <-
MSG_TRACK_GET 35 23 App wants to know the track slot? - <-
MSG_TRACK 41 29 x0x sends a track to the App? - ->
MSG_SEQ_START 48 30 App wants to press RUN? - <-
MSG_SEQ_STOP 49 31 App wants to press STOP? - <-
MSG_SEQ_GET 50 32 App wants to know if running or not? - <-
MSG_SYNC_SET 51 33 App wants to change sync mode? - <-
MSG_TEMPO_GET 64 40 App wants to know the BPM Y <-
MSG_TEMPO_SET 65 41 App wants to change the BPM Y <-
MSG_TEMPO 66 42 x0x sends BPM to the App Y ->
MSG_STATUS 128 80 Status (OK / BAD) Y <->
EXTENDED MESSAGES
MSG_FW_VER 129 81 Firmware Version (OSID) <->
MSG_ISSUPP 130 82 Is Supported? <->
MSG_DIAG 131 83 DiagOS special message <->
MSG_TEXTOUT 132 84 Text Output to App ->
MSG_GET_MEM 133 85 Get variable-sized chunk from the EEPROM <->
MSG_SET_MEM 134 86 Send variable-sized chunk to the EEPROM <-
MSG_PATBUF 135 87 Send/Get pattern(s) from the pattern buffer <-
MSG_GET_INFO 136 88 Get info <->
MSG_GET_PARAM 137 89 Get parameter <->
MSG_SET_PARAM 138 8A Set parameter <-
Note

The dark-gray messages (marked with a dash under USED) have only been #defined in the protocol (either in the stock firmware or c0ntr0l) but haven’t been implemented ever.
I’ve attempted to guess their meaning under description based on their names..

c0nb0x implements Extended messages with values above 128.

Note
The "bi-directional" messages may have different packet structure/content depending on the direction (see MSG_FW_VER as an example of this).

Extended Messages

MSG_FW_VER

c0nb0x introduces the MSG_FW_VER message, to make it possible for each firmware to be identified with it’s unique ID and name (string).
The ID can be used in the app to have custom features or handle different pattern formats.

App-to-x0x:

[MSG_FW_VER] [0] [0] [CRC]

You can see this is just an empty packet (has no data).

x0x-to-App:

This is the reply, it has the following data contents:

[osID] [VERSION] [osSTRING]
  • uint16_t osID - unique ID of the firmware

  • uint16_t VERSION - this value will be divided by 100 and printed as a fixed-point number in c0nb0x (for example, 123 → "1.23")

  • osSTRING - variable sized C-string with the name of the Firmware which will be printed in c0nb0x.
    This must be at least 1 char, but not more than 32 chars, not null-terminated.

So the data_size of this message is then (2+2+length_of_osSTRING).

Currently, c0nb0x has definitions for the following FirmWare unique IDs:
OSID_UNKNOWN    = 0, // unknown, adafruit v1.05 compatible
OSID_ADA105     = 1,
OSID_SOKKOS     = 2,
OSID_N0NX0XBETA = 13,
OSID_DIAGFW     = 101, // DiagOS
OSID_SOKKOS2561 = 20, // sokkos (cpumod)
OSID_N0NX0X     = 23, // n0nx0x2 (cpumod)
Note

If the firmware does not support the MSG_FW_VER (so it replies with STATUS_BAD message), c0nb0x looks up the pattern size (by requesting MSG_PATT_RD with bank1, slot1).

  • If the pattern size is 16 - c0nb0x assumes the firmware is OSID_ADA105

  • If the pattern size is 21 - c0nb0x assumes the firmware is OSID_N0NX0XBETA

  • Otherwise assumes OSID_UNKNOWN

In all these cases, that’s "auto-detection" and thus c0nb0x prints "(auto)" in front of the Firmware name.

Only if the Firmware replies with MSG_FW_VER will c0nb0x print the actual osSTRING.

Implementing MSG_FW_VER reply in your firmware:

Example 1. As an example, here’s how to make the message packet for n0nx0x2 (osID=23):
case MSG_FW_VER:
{
	//  2bytes    2bytes   <variable>
	// [ osID ] [ osVER ] [ os string ]
	{
		uint16_t* ptr = (uint16_t*)(tx_msg_buff+3);
		*ptr = 23; // TODO: osID
		++ptr;
		*ptr = 200; // TODO: version
	}
	unsigned char* ptr = (tx_msg_buff+(3+4));
	// os string should not be longer than 32 chars

	#define OSSTRING_LEN 6 // TODO: osSTRING_LENGTH

	for (uint8_t i = 0; i < OSSTRING_LEN; i++)
	{
		ptr[i] = "n0nx0x"[i]; // TODO: osSTRING
	}
	uint8_t tmp = 2+2+OSSTRING_LEN; // data_size
	tx_msg_buff[0] = MSG_FW_VER;
	tx_msg_buff[1] = 0;
	tx_msg_buff[2] = tmp;
	tmp += 3;
	tx_msg_buff[tmp] = calc_CRC8(tx_msg_buff, tmp);
	++tmp;
	send_msg(tx_msg_buff, tmp);
} break;

In the code above, you have to change 4 things (commented with TODO) and you’re done.

MSG_TEXTOUT

MSG_TEXTOUT will be available in c0nb0x v1.01.
Its purpose is to help you "Monitor" or debug the x0x firmware.
The direction is only x0x→App and the message can contain a string, an integer, or binary data.

x0x→App:

[MSG_TEXTOUT] [0] [size] <data> [CRC]

where the data contains:

[type] [rawdata]

type can be one of the following:

enum TEXTOUT_TYPE
{
    TXTO_STR        = 0, // null-terminated string
    TXTO_UI8        = 1, // uint8_t
    TXTO_SI8        = 2, // int8_t
    TXTO_UI16       = 3, // uint16_t
    TXTO_SI16       = 4, // int16_t
    TXTO_UI32       = 5, // uint32_t
    TXTO_SI32       = 6, // int32_t
    TXTO_BIN        = 7, // <binary data>
};

rawdata is the actual string of characters, or integer, or array of bytes of binary data.
c0nb0x will convert the integers to text (via ostringstream) and the binary data will be shown as HEX.

Here are functions for textout:

#ifdef XMSG_TEXTOUT
void textout(char* s);
void textout_bin(uint8_t* data, uint8_t len);
void textout_ui8(uint8_t x);
void textout_ui16(uint16_t x);
void textout_ui32(uint32_t x);
void textout_si8(int8_t x);
void textout_si16(int16_t x);
void textout_si32(int32_t x);
#else
    #define textout(x)
    #define textout_bin(x,y)
    #define textout_ui8(x)
    #define textout_ui16(x)
    #define textout_ui32(x)
    #define textout_si8(x)
    #define textout_si16(x)
    #define textout_si32(x)
#endif//XMSG_TEXTOUT
#ifdef XMSG_TEXTOUT
void textout(char* s)
{
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_STR;
    // get length of string
    // don't pass long strings please!
    uint8_t len = 0;
    while (len < 255)
    {
        if (s[len] == 0x00) { break; }
        ++len;
    }
    // we'll slice the string if it's too big to fit into a single packet
    uint8_t nump = len/48; // let's use 48 bytes at a time
    if ((len-(nump*48))) { ++nump; } // round-up
    uint8_t i = 0;
    uint8_t i2 = 0;
    uint8_t n = 0;

    while (n < nump)
    {
        // for each packet..
        i = 0;
        while (i < 48)
        {
            if (i2 >= len) { break; }
            tx_msg_buff[4+i] = s[i2];
            ++i;
            ++i2;
        }
        ++i;
        tx_msg_buff[2] = i; // data_size
        i += 3;
        tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
        send_msg(tx_msg_buff, 1+i);
        ++n;
    }
    return;
}
void textout_bin(uint8_t* data, uint8_t len)
{
    // binary!
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_BIN;
    // we'll slice the data if it's too big to fit into a single packet
    uint8_t nump = len/48; // let's use 48 bytes at a time
    if ((len-(nump*48))) { ++nump; } // round-up
    uint8_t i = 0;
    uint8_t i2 = 0;
    uint8_t n = 0;

    while (n < nump)
    {
        // for each packet..
        i = 0;
        while (i < 48)
        {
            if (i2 >= len) { break; }
            tx_msg_buff[4+i] = data[i2];
            ++i;
            ++i2;
        }
        ++i;
        tx_msg_buff[2] = i; // data_size
        i += 3;
        tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
        send_msg(tx_msg_buff, 1+i);
        ++n;
    }
    return;
}
void textout_ui8(uint8_t x)
{
    uint8_t len = 1;
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_UI8;
    uint8_t i = 0;
    while (i < len)
    {
        tx_msg_buff[4+i] = x;
        ++i;
    }
    ++i;
    tx_msg_buff[2] = i; // data_size
    i += 3;
    tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
    send_msg(tx_msg_buff, 1+i);
    return;
}
void textout_ui16(uint16_t x)
{
    uint8_t len = 2;
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_UI16;
    uint8_t i = 0;
    uint8_t *s = (uint8_t*)(&x);
    while (i < len)
    {
        tx_msg_buff[4+i] = s[i];
        ++i;
    }
    ++i;
    tx_msg_buff[2] = i; // data_size
    i += 3;
    tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
    send_msg(tx_msg_buff, 1+i);
    return;
}
void textout_ui32(uint32_t x)
{
    uint8_t len = 4;
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_UI32;
    uint8_t i = 0;
    uint8_t *s = (uint8_t*)(&x);
    while (i < len)
    {
        tx_msg_buff[4+i] = s[i];
        ++i;
    }
    ++i;
    tx_msg_buff[2] = i; // data_size
    i += 3;
    tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
    send_msg(tx_msg_buff, 1+i);
    return;
}
void textout_si8(int8_t x)
{
    uint8_t len = 1;
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_SI8;
    uint8_t i = 0;
    while (i < len)
    {
        tx_msg_buff[4+i] = x;
        ++i;
    }
    ++i;
    tx_msg_buff[2] = i; // data_size
    i += 3;
    tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
    send_msg(tx_msg_buff, 1+i);
    return;
}
void textout_si16(int16_t x)
{
    uint8_t len = 2;
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_SI16;
    uint8_t i = 0;
    uint8_t *s = (uint8_t*)(&x);
    while (i < len)
    {
        tx_msg_buff[4+i] = s[i];
        ++i;
    }
    ++i;
    tx_msg_buff[2] = i; // data_size
    i += 3;
    tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
    send_msg(tx_msg_buff, 1+i);
    return;
}
void textout_si32(int32_t x)
{
    uint8_t len = 4;
    tx_msg_buff[0] = MSG_TEXTOUT;
    tx_msg_buff[1] = 0;
    tx_msg_buff[3] = TXTO_SI32;
    uint8_t i = 0;
    uint8_t *s = (uint8_t*)(&x);
    while (i < len)
    {
        tx_msg_buff[4+i] = s[i];
        ++i;
    }
    ++i;
    tx_msg_buff[2] = i; // data_size
    i += 3;
    tx_msg_buff[i] = calc_CRC8(tx_msg_buff, i);
    send_msg(tx_msg_buff, 1+i);
    return;
}
#endif//XMSG_TEXTOUT
Example 2. TEXTOUT usage
if (tempo_changed)
{
    textout("Tempo changed to ");
    textout_ui16(tempo);
    textout(" bpm.\n");
}

MSG_ISSUP

This message is used to query if some messages/features are supported by the firmware.
c0nb0x will usually send a few such messages on connect to see if the FW supports this and that.

[MSG_ISSUP] [0] [2] <CODE> [CRC]
  • uint16_t CODE - this value is normally a msgID, but it’s two-byte for possible future extension.

Example 3. Querying if the firmware supports MSG_ISSUP:
[MSG_ISSUP] [0] [2] [0, MSG_ISSUP] [CRC]

That’s how c0nb0x will ask if the FW even supports the MSG_ISSUP itself.

The x0x will reply with:

[MSG_ISSUP] [0] [3] <CODE> <ANSWER> [CRC]
  • CODE must have the same value as the requested one.

  • ANSWER is 0 for "not supported" or 1 for "supported" (higher values may be used in some special cases).

Normally, c0nb0x will not query about the "normal" messages (those below 128) so they don’t need to return a positive answer.
c0nb0x needs to know the pattern size, and on startup, requests MSG_PATT_RD just for that reason.

From c0nb0x v1.01, another method is used: MSG_ISSUP with MSG_PATT as the argument - the x0x should reply with PATT_SIZE as the answer, then c0nb0x will not request MSG_PATT_RD.

MSG_GET_MEM

Other than MSG_PATT_WR and MSG_PATT_RD, there is really no other way to get/set data from/to the EEPROM via the App.
So here it is.

App→x0x:

[MSG_GET_MEM] [0] [3] <ADDRESS> <LENGTH> [CRC]
  • uint16_t ADDRESS - the address (or offset) in the EEPROM memory.

  • uint8_t LENGTH - the number of bytes the App wants to get.

Warning
The App should never try to request big amounts of data at once, the x0x should refuse such requests.
Instead, the app should make multiple requests using smaller packets.

x0x→App:

[MSG_GET_MEM] [0] [size] <ADDRESS> <DATA> [CRC]
  • uint16_t ADDRESS must be the same value as in the request.

  • DATA is the actual data.

  • size is the LENGTH of the data + 2.

Note
If the x0x refuses to reply to a request - it should send a MSG_STATUS with STAT_BAD.

MSG_SET_MEM

App→x0x:

[MSG_SET_MEM] [0] [size] <ADDRESS> <DATA> [CRC]
  • uint16_t ADDRESS - the address (or offset) in the EEPROM memory.

  • DATA - the actual data to be written.

  • size - the LENGTH of the data + 2.

Note
The App should NOT send too much data at once, so the x0x is free to refuse to process such requests.

The x0x must reply with MSG_STATUS with either STAT_OK or STAT_BAD.

MSG_PATBUF

c0nb0x has a pattern editor. Up till v1.00, the pattern editor uses MSG_PATT_RD and MSG_PATT_WR to load/save the pattern from the x0xb0x.
This means that the patterns were written to EEPROM everytime, especially with the "Auto L/S" option, that’s quite an abuse for the EEPROM.
So, to end this abuse, the MSG_PATBUF message was introduced.
Instead of loading/saving (directly from/to the EEPROM) the pattern you edit - this can be done from the pattern buffer (which is in RAM).

The downsides are:

  • The pattern is not really "saved" unless the firmware saves it

  • The pattern editor in c0nb0x cannot load/save from/to a specific bank & slot

The benefits are:

  • Hear the changes to the pattern as it plays instantly (how this will be implemented is up to the firmware)

  • Use "Auto L/S" without worrying about abusing the EEPROM.

In the stock firmware, the pattern buffer holds only 1 pattern, but in other FWs it might hold more. Thus the message supports that scenario, but c0nb0x will not implement it, as the pattern editor in c0nb0x currently works with one pattern at a time.

To get a pattern from the pattern buffer:
App→x0x:

[MSG_PATBUF] [0] [2] <0> <N> [CRC]
  • byte 4 is 0 for "get"

  • N is the pattern index in the patbuf (if it holds more than one pattern) .. starts from 0.

x0x→App:

The x0x must reply with MSG_PATT containing the requested pattern, or MSG_STATUS with STAT_BAD if something goes wrong.

To send a pattern to the pattern buffer:
App→x0x:

[MSG_PATBUF] [0] [size] <1> <N> <DATA> [CRC]
  • size is (2+PATT_SIZE).

  • byte 4 is 1 for "set"

  • N is the pattern index in the patbuf (if it holds more than one pattern) .. starts from 0.

  • DATA is the actual pattern to be written to the pattern buffer.

x0x→App:

The x0x must reply with MSG_STATUS with either STAT_OK or STAT_BAD.

MSG_GET_INFO

This message is used to query some useful information about the firmware.
It will be available in c0nb0x v1.01.

App→x0x:

[MSG_GET_INFO] [0] [1] <CODE> [CRC]
  • uint8_t CODE - see examples below

Example 4. Get info about the EEPROMs:
[MSG_GET_INFO] [0] [1] <0> [CRC]

The x0x should reply with:

[MSG_GET_INFO] [0] [4] <INTERNAL> <EXTERNAL> [CRC]
  • uint16_t INTERNAL - size of the internal EEPROM.
    The internal EEPROM is not used by c0nb0x yet, but might be used in the future.

  • uint16_t EXTERNAL - size of the external EEPROM (aka pattern memory).

The EXTERNAL eeprom is 4kB by design.
However, the CPUMOD has a slot for an additional 4kB EEPROM which may be present. In that case, the firmware can return 8kB. For this purpose, the implementation of MSG_GET_MEM and MSG_SET_MEM (or MSG_PATT_RD and MSG_PATT_WR) should be changed to operate on the second EEPROM for addresses above 4095.

Example 5. Get info about the pattern count:
[MSG_GET_INFO] [0] [1] <1> [CRC]

The x0x should reply with:

[MSG_GET_INFO] [0] [2] <BANKS> <SLOTS> [CRC]
  • uint8_t BANKS - number of banks (e.g. 16)

  • uint8_t SLOTS - number of pattern slots (per bank)

Thus the number of patterns is (BANKS*SLOTS), which will be used for the "Import/Export Patterns" function in c0nb0x.

Example 6. Is this a CPUMOD?
[MSG_GET_INFO] [0] [1] <8> [CRC]

If this is a firmware running on the CPUMOD (atmega2561) the x0x should reply with:

[MSG_GET_INFO] [0] [1] <1> [CRC]

Parameters

c0nb0x v1.02 introduces two new messages into the protocol: MSG_GET_PARAM and MSG_SET_PARAM.
The firmware can have up to 1024 parameters, which can be changed by the user from a menu in c0nb0x.

Parameters are represented as int16_t values.
There are 3 parameter types: BOOL, INT, ENUM.

  • BOOL: an On/Off-type parameter

  • INT: a signed integer

  • ENUM: a parameter where each value has a custom label

c0nb0x uses a special external file (*.prm) which describes each parameter, what type it is, what range it has, and what labels/info text to print on screen.

Adding Parameter support to your firmware:

  • Your FW must support at least MSG_FW_VER, and MSG_ISSUPP.

  • On connect, c0nb0x would ask if MSG_GET_PARAM is supported.

    • If it is, c0nb0x will send a MSG_GET_PARAM with param index 0xFFFF.
      The firmware should reply with a MSG_GET_PARAM containing: [uint16_t num_params] [uint16_t params_unique_version].

If all that is successiful, c0nb0x will generate a unique *.prm filename for the firmware, and attempt to load that file from the main c0nb0x directory.

The filename will contain the firmware name string (exotic chars would be stripped, A-Z would be turned into a-z, and 0-9), the OSID, the number of params, and the unique params version.
This would make it possible to support multiple versions of the same firmware, even if those versions have a different set of parameters and/or parameter ordering.

Example filename: n0nx0x_23_0_18.prm

name string

"n0nx0x"

OSID

23

params_unique_version

0

num_params

18

The *.prm file would have to be distributed together with the firmware, and "installed" into the c0nb0x main directory.

If c0nb0x doesn’t find the file, or something goes wrong during the parsing (wrong number of params, bad syntax.. etc..), the Parameters menu will be unaccessible to the user.

MSG_GET_PARAM

App→x0x:

[MSG_GET_PARAM] [0] [2] <param_index> [CRC]
  • uint16_t param_index - 0 to (num_params-1)

The FW should reply with:
x0x→App:

[MSG_GET_PARAM] [0] [4] <param_index> <value> [CRC]
  • uint16_t value - the actual value

…​or with MSG_STATUS with STAT_BAD.

MSG_SET_PARAM

When the App wants to change a parameter:
App→x0x:

[MSG_SET_PARAM] [0] [4] <param_index> <value> [CRC]

The x0x should reply with either a MSG_GET_PARAM echoing back the value, or with a MSG_STATUS with STAT_BAD.
The x0x may reject the parameter change.

On CONNECT

Here’s an overview of what happens when c0nb0x connects to the x0xb0x.

Note
this is valid for c0nb0x v1.01.
Note
this does not apply for when connecting to upload firmware.
The bootloader would freak out with such data.

- Serial port opened successifully -


MSG_PING

MSG_FW_VER

MSG_ISSUP: "MSG_ISSUP"

If MSG_ISSUP is supported:

  • MSG_ISSUP: "MSG_PATT" (the x0x should return the pattern size)

  • MSG_ISSUP: "MSG_GET_MEM"

  • MSG_ISSUP: "MSG_SET_MEM"

  • MSG_ISSUP: "MSG_PATBUF"

  • MSG_ISSUP: "MSG_GET_INFO"

If MSG_GET_INFO is supported:

  • MSG_GET_INFO: 0 (eeprom info)

  • MSG_GET_INFO: 1 (pattern banks/slots info)

  • MSG_GET_INFO: 8 (is you is, or is you ain’t a CPUMOD)

If the pattern size was not detected yet:

  • MSG_PATT_RD: 0, 0 (get the pattern at bank 1, slot 1)

If the OSID wasn’t detected yet - guess it based on the collected info so far.

Guess the pattern format.

MSG_TEMPO_GET


- Connected -

CXM memory files

c0nb0x uses .cxm files for the EEPROM memory and Pattern memory import/export.
CXM is just a raw data file like the .xbp (used in c0ntr0l), but with a prepended header.

Table 1. CXM file structure
description offset type name offset end

Magic Word

0

char[8]

"c0nb0x/M"

8

char '\n'

9

uint16_t

data_offset

11

char '\n'

12

uint16_t

os_id

14

char '\n'

15

uint16_t

pattern_size

17

char '\n'

Raw data

18

the remaining data is the same as in .xbp

The idea behind this is to store the OS_ID into the file.
This makes it possible for c0nb0x to figure out what format the patterns in the file are, for when you try to import them into another firmware which uses a different pattern format.