ムライボックス (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; ...)