ムライボックス (16) --- ソフトウェア (8)

前回の記事では省略した、システム・エクスクルーシブ・メッセージ処理部のプログラム・リストを下に示します。
「0xf8u」のようにわざわざ「unsigned」を指定していたり、(uint8_t) などのキャストをしているのは、8 ビット PIC 用の XC8 コンパイラ

  warning: (373) implicit signed to unsigned to conversion

というウォーニング・メッセージを出さないようにするためのもので、本質的なものではありません。

// not System Exclusive mode flag value
#define NO_SYSX (-1)

// System Exclusive flag and buffer index
int8_t sysx_ind = NO_SYSX; // initially no sysx mode

// header length of
// XG Display Bitmap Data System Exclusive
// (0xf0 and address Mid/Low not included)
#define SYSX_HDR_LEN (4u)

// header of XG Display Bitmap Data message
uint8_t sysx_hdr[SYSX_HDR_LEN] = {
// 0xf0 :   Start Of Exclusive (not icluded)
   0x43, // YAMAHA ID
   0x1f, // device number = 16
   0x4c, // XG  model ID
   0x07  // address High (XG display bitmap data)
//   vh :   address Mid  (vert/horiz)
//   00 :   address Low          
}; // uint8_t sysx_hdr[]

// buffer size for XG display bitmap system exclusive
// (0xf0, 0xf7 ommitted)
#define SYSX_BUF_LEN (SYSX_HDR_LEN + 2 + (3u * 16u))

uint8_t sysx_buf[SYSX_BUF_LEN];

// table of pointer to remap/bitmask table
uint8_t *(tab_list[3]) = {
  remap_tab,
  mask_tab,
  sysmsg_tab
}; // uint8_t tab_list[][]

// load remap/mask table
// by System Exclusive message
void load_mask(uint8_t n)
{
  uint8_t *p; // remap/mask table pointer
  uint8_t bm; // assembled bitmap

// extract "vertical" part (b5..b4)
  n = (0x03u & (uint8_t)(n >> 4));
  if (2 < n) { n = 2; } // validate n = 0..2  
  p = tab_list[n]; // get remap/mask table pointer
  for (uint8_t i = 0; i < 16; i++) { // assemble bitmap data
// upper 8 bits of the bitmap not used    
    bm  = (0xfcu & (uint8_t)(sysx_buf[i+SYSX_HDR_LEN+2+16] << 2)); // b7..b2
    bm |= (0x03u & (uint8_t)(sysx_buf[i+SYSX_HDR_LEN+2+32] >> 5)); // b1..b0
    p[i] = bm;
   } // for (int i = 0; ...)
} // void load_mask()

//
// System Exclusive message processing
//
void sysx_proc(uint8_t c)
{
 // ignore System Realtime message
  if (0xf8u <= c) { return; }
// channel message or system common/exclusive message  
  if (0x80u <= c) { // status byte
    if (0xf7u == c) { // EOX
      if (((int8_t)SYSX_BUF_LEN <= sysx_ind) && // sufficient data received
          (0 == memcmp(sysx_hdr, 
                       sysx_buf, 
                       sizeof(sysx_hdr)))) { // header match
        load_mask(sysx_buf[SYSX_HDR_LEN]);
      } // if ( 0 == memcmp()) { ...
      sysx_ind = NO_SYSX; // leave sysx mode
    } else if (0xf0u == c) { // SOX
      sysx_ind = 0;  // enter sysx mode
    } else { // other status byte
      sysx_ind = NO_SYSX; // leave sysx mode
    } // if (0xf7u == c) { ...
  } else { // data byte
    if (0 <= sysx_ind) { // copy MIDI byte to sysx buffer
      if (SYSX_BUF_LEN <= (uint8_t)sysx_ind) { // sysx buffer full
        sysx_ind = NO_SYSX; // leave sysx mode
      } else { // buffer has room
        sysx_buf[(uint8_t)sysx_ind++] = c;
       } // if (SYSX_BUF_LEN <= sysx_ind) { ...
    } // if (0 <= sysx_ind) { ...
  } // if (0x80u <= c) { ...
} // void sysx_proc()

メインの無限ループの中で、UART TX 送信レジスタに送信キャラクタを書き込んだ直後に、

   sysx_proc(c);

としてシステム・エクスクルーシブ・メッセージ処理ルーチンが呼ばれます。
引数として渡される変数「c」は、「リマップ」処理により、チャネル・メッセージのステータス・バイトの場合には、すでにチャネル番号が書き換えられてしまった後のものですが、システム・エクスクルーシブの処理では SOX (Start Of Exclusive) 0xf0、EOX (End Of Exclusive) 0xf7 とデータ・バイトだけが重要なので、全く問題ありません。
sysx_proc() 関数では、まず、システム・リアルタイム・メッセージ (0xf8 〜 0xff) のステータス・バイトであることを検出すると、何もしないでリターンします。
システム・リアルタイム・メッセージはどこでも、システム・エクスクルーシブのデータ・バイト列の中にも現れることが許されており、その対応としては、単に無視します。
次にステータス・バイトとデータ・バイトで処理を分岐し、ステータス・バイトの分岐の中では、SOX (0xf0) を見つけると「システム・エクスクルーシブ・モード」に入り、後続のデータ・バイト列をバッファ (sysx_buf[]) にコピーしていきます。(SOX (0xf0) 自体はコピーしない)
このバッファのインデクスおよびモード・フラグとして int8_t 型の「sysx_ind」変数を使っています。
正の値、つまり値が 0 〜 127 の間の場合にはバッファのインデクスを表し、負の数値の場合にはエクスクルーシブ・モードではないことを表しています。
用意してあるバッファの長さ (SYSX_BUF_LEN) よりデータ・バイト列が長くなる場合にはエクスクルーシブ・モードから出てコピーは中止し、そのシステム・エクスクルーシブ・メッセージ全体を無視します。
EOX (0xf7) を見つけると、これまでコピーしてきたデータ・バイト列の長さと想定しているシステム・エクスクルーシブ・メッセージの長さが等しいかどうかを判定し、等しければヘッダ部分の比較に入ります。
目的の「XG Display Bitmap Data」メッセージは、

    0xf0 0x43 0x1f 0x4c 0x07 vh 0x00 ...

というシーケンスになっていますから、先頭の 0xf0 を除き、0x43 から 0x07 までの 4 バイトを比較します。
一致すれば、それは目的のシステム・エクスクルーシブ・メッセージということですから、リマップ/ビットマスク・テーブル書き換えのための「load_mask()」関数を呼び出します。
このとき、テーブルの種別を表す引数としてバッファの 5 バイト目の「vh」バイトを渡します。
上位ニブル (b7..b4) をテーブル種別と定義したので、b5..b4 を抜き出し、0 〜 2 の範囲に制限します。
3 種のテーブルは、

// table of pointer to remap/bitmask table
uint8_t *(tab_list[3]) = {
  remap_tab,
  mask_tab,
  sysmsg_tab
}; // uint8_t *(tab_list[])

という形でリマップ/ビットマスク・テーブルへの「ポインタのテーブル」を定義しておいて利用しています。
7:7:2 に分割された 16 ビット幅ビットマップの「組み立て」は下のように行なっています。

  for (uint8_t i = 0; i < 16; i++) { // assemble bitmap data
// upper 8 bits of the bitmap not used    
    bm  = (0xfcu & (uint8_t)(sysx_buf[i+SYSX_HDR_LEN+2+16] << 2)); // b7..b2
    bm |= (0x03u & (uint8_t)(sysx_buf[i+SYSX_HDR_LEN+2+32] >> 5)); // b1..b0
    p[i] = bm;
   } // for (int i = 0; ...)

PIC 用のプログラムではテーブルのビット幅を 8 ビットに制限しているので、上のリストでも上位 8 ビット分の処理は省略しています。
他のプロセッサ用の 16 ビット・フルの処理は下のようになります。

  for (uint8_t i = 0; i < 16; i++) { // assemble bitmap data
    bm  = (0xfe00 & (sysx_buf[i+SYSX_HDR_LEN+2   ] << 9)); // b15..b9
    bm |= (0x01fc & (sysx_buf[i+SYSX_HDR_LEN+2+16] << 2)); // b8..b2
    bm |= (0x0003 & (sysx_buf[i+SYSX_HDR_LEN+2+32] >> 5)); // b1..b0
    p[i] = bm; // assembled bitmap
   } // for (int i = 0; ...)