これは月刊電脳倶楽部 138 号 (1999 年 11 月号) の読み物横町のコーナーに収録した『X680x0 アセンブラ講座 #$11《ビットフィールド命令》』を再編集したものです。
「M68000 ファミリのビットフィールド命令がよくわからない」という声を耳にすることがあるので、今回はビットフィールド命令について解説する。
ビットフィールド命令は MC68020 で新設された命令だ。MC68020~MC68060 で使うことができるが、残念ながら MC68000 では使うことができない。そのためビットフィールド命令は MC68000 のユーザにはほとんど馴染みがないものだが、他の人が作ったプログラムを解析しているときに知らない命令が出てくると困ってしまうので、一応どんなものか知っておいて損はないと思う。
ビットフィールド命令の動作は Motorola などのマニュアルを見てもよくわからなかったという人もいると思う。ここではなるべくわかるように説明したい。
ビットフィールドとは、データレジスタまたはメモリ上の連続する 1~32 ビットの領域である。C を使いこなしている人なら、構造体のビットフィールドメンバ 1 個分の領域を想像すればよい。一般的に、メモリにデータを格納するときはバイト境界に合わせて格納したほうが効率がよくなるものだが、限られた領域により多くのデータを詰め込みたい場合にはバイト境界に構わず配置されたビットフィールドを利用することがある。
簡単なビットフィールドの例を示そう(図の中の点線はバイト境界、破線はビットフィールドの境界)。
それぞれ、X、Y、Zの 3 個が幅が 10 ビットのビットフィールドであると言える。
ビットフィールド命令とは、その名の通り「ビットフィールドを扱う命令」である。MC68000~MC68060 で使うことができるビット操作命令(BCHG
/BCLR
/BSET
/BTST
)が 1 ビットの領域を扱うのに対して、MC68020~MC68060 で使うことができるビットフィールド命令は 1~32 ビットの任意の幅の領域(ビットフィールド)を扱うことができる。
ビットフィールドを扱うということは、レジスタやメモリ上で、バイト境界に関係なく任意の位置から始まる任意の幅の領域を読み出したり書き換えたりするということだ。ビット操作命令と同様に、ビットフィールドと同じバイトに含まれているビットであってもビットフィールドの外側ならばその内容はビットフィールド命令によって使用されることも変化することもない。ビットフィールド命令は、ビット単位で指定された範囲のビットフィールドだけを操作してくれる便利な命令なのだ。
個々のビットフィールド命令については後で詳しく説明することにして、先にビットフィールド命令に指定するビットフィールド・オペランドについて解説しよう。
ビットフィールド命令では、ソースオペランドまたはデスティネーションオペランドのどちらかでビットフィールドを指定しなければならない。そのオペランドをここではビットフィールド・オペランドと呼ぶことにしよう。アセンブラでは、ビットフィールド・オペランドを次のようなフォーマットで記述する。
<ea>{offset:width}
<ea>
は他の命令のオペランドの説明でも使われる実効アドレス(effective address)の指定だ。ビットフィールド・オペランドに指定できるアドレッシングモードは、データレジスタ直接、アドレスレジスタ間接(ポストインクリメント、プレデクリメントを除く)、絶対アドレス、プログラムカウンタ間接(一部のビットフィールド命令を除く)である。
「{
」「:
」「}
」の 3 つの文字は実際にアセンブラのプログラムに記述する文字だ。ビットフィールド・オペランドにはこれらの記号がなければならない。なお、他の種類のオペランドと同様に、これらの記号の前後に余計な空白を入れてはいけない。
offset
には、<ea>
で指定した実効アドレスの最上位ビットからビットフィールドの開始位置までのオフセット(ビット数)を 0~31 の定数で指定する。イミディエイトオペランドと同様に、定数の場合は手前に「#
」を付けてもよい。
offset
のところにデータレジスタを指定すると、実行時のそのデータレジスタの下位 5 ビットの内容がオフセットになる(下位 5 ビット以外のビットは無視される)。
width
には、ビットフィールドの幅(ビット数)を 1~32 の定数で指定する。イミディエイトオペランドと同様に、定数の場合は手前に「#
」を付けてもよい。
width
のところにデータレジスタを指定すると、実行時のそのデータレジスタの下位 5 ビットの内容(0 は 32 と見なされる)がビットフィールドの幅になる(下位 5 ビット以外のビットは無視される)。
!!!注意!!!
ビット操作命令のビット位置の指定と異なり、ビットフィールドの開始位置を示す offset
の値は最上位ビットからのオフセットである。ビット番号ではない。例えば、ビット操作命令
BTST.L #0,D0
でテストされるビットは D0
レジスタの最下位ビットだが、ビットフィールド・オペランド
D0{0:1}
は D0
レジスタの最上位ビットを示している。D0
レジスタの最下位ビットを示すビットフィールド・オペランドは
D0{31:1}
である。
最初に挙げたビットフィールドの例をもう一度見てみよう。
これにビットフィールドのオフセットと幅を書き加えると次のようになる。
X、Y、Zを示すビットフィールド・オペランドは、次のようになる。
ビットフィールド | オフセット | 幅 | ビットフィールド・オペランド |
---|---|---|---|
X | 2 から | 10 ビット | D0{2:10} |
Y | 12 から | 10 ビット | D0{12:10} |
Z | 22 から | 10 ビット | D0{22:10} |
ところで、言うまでもないことだが、データレジスタは 32 ビットしかない。それでは、次のビットフィールド・オペランドは何を意味しているだろうか。
D0{16:32}
オフセットが 16 で幅が 32 ビット。これではビットフィールドが D0
レジスタの最下位からはみ出してしまう。こういうときは、D0
レジスタを横に 2 つ並べて考えればよい。
このように、ビットフィールドがデータレジスタの最下位からはみ出したら最上位に戻ってくればよいのだ。ビットフィールド・オペランド D0{16:32}
は、D0
レジスタの上位ワードと下位ワードを入れ換えたものということになる。
ここでも最初に挙げた例を見てみよう。
ここでは、+0 で示したアドレスが A0
で指されている場合を考えてみる。オフセットとビットフィールドの幅も書き加えよう。
X、Y、Zを示すビットフィールド・オペランドは、次のようになる。
ビットフィールド | アドレス | オフセット | 幅 | ビットフィールド・オペランド |
---|---|---|---|---|
X | (A0) | 0 から | 10 ビット | (A0){0:10} |
Y | 1(A0) | 2 から | 10 ビット | 1(A0){2:10} |
Z | 2(A0) | 4 から | 10 ビット | 2(A0){4:10} |
実は、これらのビットフィールド・オペランドは、次のように書くこともできる。
ビットフィールド | アドレス | オフセット | 幅 | ビットフィールド・オペランド |
---|---|---|---|---|
X | (A0) | 0 から | 10 ビット | (A0){0:10} |
Y | (A0) | 10 から | 10 ビット | (A0){10:10} |
Z | (A0) | 20 から | 10 ビット | (A0){20:10} |
ビット操作命令ではメモリに関するビット番号の指定は 0~7 の範囲でしか指定できないので、MC68000 のアセンブラに慣れていると、ビットフィールド・オペランドのオフセット 10 や 20 という表記には少々違和感を感じるものだ。実効アドレスで指定されたアドレスの 1 バイトは操作の対象にすらなっていないのだ。
このように、ビットフィールド・オペランドの実効アドレスがメモリの場合は、メモリ上の任意のアドレスの任意のビットからビット番号降順、アドレス昇順で最大 32 ビットまでの領域(ビットフィールド)が操作の対象になる。データレジスタの場合と違い、ある領域の最下位からはみ出して同じ領域の最上位に戻ってくるということはない。
ビットフィールド命令には次の 8 種類がある。
BFCHG <ea>{offset:width}
BFCLR <ea>{offset:width}
BFEXTS <ea>{offset:width},Dn
BFEXTU <ea>{offset:width},Dn
BFFFO <ea>{offset:width},Dn
BFINS Dn,<ea>{offset:width}
BFSET <ea>{offset:width}
BFTST <ea>{offset:width}
なお、ビット操作命令と違ってビットフィールド命令にはオペレーションサイズがない。ビットフィールド命令にオペレーションサイズを書くとエラーになるので注意。
続いて、個々のビットフィールド命令について解説する。
BFCHG
は、BCHG
のビットフィールド版。ビットフィールド全体をビットごとに反転する。
BFCHG <ea>{offset:width}
BFCHG
命令で使用できるアドレッシングモードは以下の通り。
BFCHG Dn{offset:width}
BFCHG (An){offset:width} BFCHG (d16,An){offset:width} BFCHG (d8,An,Xi){offset:width} BFCHG (bd,An,Xi){offset:width} BFCHG ([bd,An,Xi]){offset:width} BFCHG ([bd,An,Xi],od.W){offset:width} BFCHG ([bd,An,Xi],od.L){offset:width} BFCHG ([bd,An],Xi){offset:width} BFCHG ([bd,An],Xi,od.W){offset:width} BFCHG ([bd,An],Xi,od.L){offset:width} BFCHG (bd,An){offset:width} BFCHG ([bd,An]){offset:width} BFCHG ([bd,An],od.W){offset:width} BFCHG ([bd,An],od.L){offset:width}
BFCHG (xxx).W{offset:width} BFCHG (xxx).L{offset:width}
操作前のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
BFCHG D0{0:16}
D0
の上位ワードがビット反転される
BFCLR
は、BCLR
のビットフィールド版。ビットフィールド全体をクリアする。
BFCLR <ea>{offset:width}
BFCLR
命令で使用できるアドレッシングモードは以下の通り。
BFCLR Dn{offset:width}
BFCLR (An){offset:width} BFCLR (d16,An){offset:width} BFCLR (d8,An,Xi){offset:width} BFCLR (bd,An,Xi){offset:width} BFCLR ([bd,An,Xi]){offset:width} BFCLR ([bd,An,Xi],od.W){offset:width} BFCLR ([bd,An,Xi],od.L){offset:width} BFCLR ([bd,An],Xi){offset:width} BFCLR ([bd,An],Xi,od.W){offset:width} BFCLR ([bd,An],Xi,od.L){offset:width} BFCLR (bd,An){offset:width} BFCLR ([bd,An]){offset:width} BFCLR ([bd,An],od.W){offset:width} BFCLR ([bd,An],od.L){offset:width}
BFCLR (xxx).W{offset:width} BFCLR (xxx).L{offset:width}
操作前のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
BFCLR D0{0:16}
D0
の上位ワードがクリアされる
BFEXTS
は、ビットフィールドの内容を符号つき整数と見なして取り出す。
BFEXTS <ea>{offset:width},Dn
Dn
に格納する。BFEXTS
命令で使用できるアドレッシングモードは以下の通り。
BFEXTS Dn{offset:width}
BFEXTS (An){offset:width} BFEXTS (d16,An){offset:width} BFEXTS (d8,An,Xi){offset:width} BFEXTS (bd,An,Xi){offset:width} BFEXTS ([bd,An,Xi]){offset:width} BFEXTS ([bd,An,Xi],od.W){offset:width} BFEXTS ([bd,An,Xi],od.L){offset:width} BFEXTS ([bd,An],Xi){offset:width} BFEXTS ([bd,An],Xi,od.W){offset:width} BFEXTS ([bd,An],Xi,od.L){offset:width} BFEXTS (bd,An){offset:width} BFEXTS ([bd,An]){offset:width} BFEXTS ([bd,An],od.W){offset:width} BFEXTS ([bd,An],od.L){offset:width}
BFEXTS (xxx).W{offset:width} BFEXTS (xxx).L{offset:width}
BFEXTS (d16,PC){offset:width} BFEXTS (d8,PC,Xi){offset:width} BFEXTS (bd,PC,Xi){offset:width} BFEXTS ([bd,PC,Xi]){offset:width} BFEXTS ([bd,PC,Xi],od.W){offset:width} BFEXTS ([bd,PC,Xi],od.L){offset:width} BFEXTS ([bd,PC],Xi){offset:width} BFEXTS ([bd,PC],Xi,od.W){offset:width} BFEXTS ([bd,PC],Xi,od.L){offset:width} BFEXTS (bd,PC){offset:width} BFEXTS ([bd,PC]){offset:width} BFEXTS ([bd,PC],od.W){offset:width} BFEXTS ([bd,PC],od.L){offset:width}
操作前のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
BFEXTS D0{16:8},D0
D0
の下位ワードの上位バイトを 32 ビットに符号拡張して D0
に格納し直す
BFEXTU
は、ビットフィールドの内容を符号なし整数と見なして取り出す。
BFEXTU <ea>{offset:width},Dn
Dn
に格納する。BFEXTU
命令で使用できるアドレッシングモードは以下の通り。
BFEXTU Dn{offset:width}
BFEXTU (An){offset:width} BFEXTU (d16,An){offset:width} BFEXTU (d8,An,Xi){offset:width} BFEXTU (bd,An,Xi){offset:width} BFEXTU ([bd,An,Xi]){offset:width} BFEXTU ([bd,An,Xi],od.W){offset:width} BFEXTU ([bd,An,Xi],od.L){offset:width} BFEXTU ([bd,An],Xi){offset:width} BFEXTU ([bd,An],Xi,od.W){offset:width} BFEXTU ([bd,An],Xi,od.L){offset:width} BFEXTU (bd,An){offset:width} BFEXTU ([bd,An]){offset:width} BFEXTU ([bd,An],od.W){offset:width} BFEXTU ([bd,An],od.L){offset:width}
BFEXTU (xxx).W{offset:width} BFEXTU (xxx).L{offset:width}
BFEXTU (d16,PC){offset:width} BFEXTU (d8,PC,Xi){offset:width} BFEXTU (bd,PC,Xi){offset:width} BFEXTU ([bd,PC,Xi]){offset:width} BFEXTU ([bd,PC,Xi],od.W){offset:width} BFEXTU ([bd,PC,Xi],od.L){offset:width} BFEXTU ([bd,PC],Xi){offset:width} BFEXTU ([bd,PC],Xi,od.W){offset:width} BFEXTU ([bd,PC],Xi,od.L){offset:width} BFEXTU (bd,PC){offset:width} BFEXTU ([bd,PC]){offset:width} BFEXTU ([bd,PC],od.W){offset:width} BFEXTU ([bd,PC],od.L){offset:width}
操作前のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
BFEXTU D0{16:8},D0
D0
の下位ワードの上位バイトを 32 ビットにゼロ拡張して D0
に格納し直す
BFEXTU D0{16:32},D0
D0
の上位ワードと下位ワードを入れ換える
BFFFO
は、ビットフィールドの中で最初の 1 のビットを探す。
BFFFO <ea>{offset:width},Dn
offset
と同様に実効アドレスの最上位ビットからのオフセット)を 32 ビットにゼロ拡張して Dn
に格納する。ビットフィールドの中の最上位のビットが立っているときは、Dn
には offset
の値が入る。ビットフィールドの中のすべてのビットが 0 のときは、Dn
には offset
+width
の値が入る。BFFFO
命令で使用できるアドレッシングモードは以下の通り。
BFFFO Dn{offset:width}
BFFFO (An){offset:width} BFFFO (d16,An){offset:width} BFFFO (d8,An,Xi){offset:width} BFFFO (bd,An,Xi){offset:width} BFFFO ([bd,An,Xi]){offset:width} BFFFO ([bd,An,Xi],od.W){offset:width} BFFFO ([bd,An,Xi],od.L){offset:width} BFFFO ([bd,An],Xi){offset:width} BFFFO ([bd,An],Xi,od.W){offset:width} BFFFO ([bd,An],Xi,od.L){offset:width} BFFFO (bd,An){offset:width} BFFFO ([bd,An]){offset:width} BFFFO ([bd,An],od.W){offset:width} BFFFO ([bd,An],od.L){offset:width}
BFFFO (xxx).W{offset:width} BFFFO (xxx).L{offset:width}
BFFFO (d16,PC){offset:width} BFFFO (d8,PC,Xi){offset:width} BFFFO (bd,PC,Xi){offset:width} BFFFO ([bd,PC,Xi]){offset:width} BFFFO ([bd,PC,Xi],od.W){offset:width} BFFFO ([bd,PC,Xi],od.L){offset:width} BFFFO ([bd,PC],Xi){offset:width} BFFFO ([bd,PC],Xi,od.W){offset:width} BFFFO ([bd,PC],Xi,od.L){offset:width} BFFFO (bd,PC){offset:width} BFFFO ([bd,PC]){offset:width} BFFFO ([bd,PC],od.W){offset:width} BFFFO ([bd,PC],od.L){offset:width}
操作前のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
MOVE.L #$00084210,D0 BFFFO D0{4:20},D0
ビットフィールドの内容は $00842
であり、最上位の 1 のオフセットは 12 なので、D0
=$0000000C
になる。
BFFFO D0{0:32},D1 LSL.L D1,D0
D0
の内容を左詰めにする。
BFINS
は、データをビットフィールドに書き込む。
BFINS Dn,<ea>{offset:width}
Dn
の下位から width
で示されたビット数だけ取り出してビットフィールドに上書きする。!!!注意!!!
他のビットフィールド命令は操作前のビットフィールドの内容をテストするが、BFINS
は操作後のビットフィールドの内容をテストする。
BFINS
命令で使用できるアドレッシングモードは以下の通り。
BFINS Dn,Dn{offset:width}
BFINS Dn,(An){offset:width} BFINS Dn,(d16,An){offset:width} BFINS Dn,(d8,An,Xi){offset:width} BFINS Dn,(bd,An,Xi){offset:width} BFINS Dn,([bd,An,Xi]){offset:width} BFINS Dn,([bd,An,Xi],od.W){offset:width} BFINS Dn,([bd,An,Xi],od.L){offset:width} BFINS Dn,([bd,An],Xi){offset:width} BFINS Dn,([bd,An],Xi,od.W){offset:width} BFINS Dn,([bd,An],Xi,od.L){offset:width} BFINS Dn,(bd,An){offset:width} BFINS Dn,([bd,An]){offset:width} BFINS Dn,([bd,An],od.W){offset:width} BFINS Dn,([bd,An],od.L){offset:width}
BFINS Dn,(xxx).W{offset:width} BFINS Dn,(xxx).L{offset:width}
操作後のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
MOVE.L #$12345678,D0 BFINS D0,(A0){4:16}
(A0)
から 3 バイトが $?5,$67,$8?
になる(?
のところは変化しない)
BFSET
は、BSET
のビットフィールド版。ビットフィールド全体をセットする。
BFSET <ea>{offset:width}
BFSET
命令で使用できるアドレッシングモードは以下の通り。
BFSET Dn{offset:width}
BFSET (An){offset:width} BFSET (d16,An){offset:width} BFSET (d8,An,Xi){offset:width} BFSET (bd,An,Xi){offset:width} BFSET ([bd,An,Xi]){offset:width} BFSET ([bd,An,Xi],od.W){offset:width} BFSET ([bd,An,Xi],od.L){offset:width} BFSET ([bd,An],Xi){offset:width} BFSET ([bd,An],Xi,od.W){offset:width} BFSET ([bd,An],Xi,od.L){offset:width} BFSET (bd,An){offset:width} BFSET ([bd,An]){offset:width} BFSET ([bd,An],od.W){offset:width} BFSET ([bd,An],od.L){offset:width}
BFSET (xxx).W{offset:width} BFSET (xxx).L{offset:width}
操作前のビットフィールドの内容に応じてフラグが変化する。詳細は後述。
MOVEQ.L #4,D0 BFSET (A0){D0:16}
(A0)
から 3 バイトが $?F,$FF,$F?
になる(?
のところは変化しない)
BFTST
は、BTST
のビットフィールド版。ビットフィールド全体をテストする。
BFTST <ea>{offset:width}
BFTST
命令で使用できるアドレッシングモードは以下の通り。
BFTST Dn{offset:width}
BFTST (An){offset:width} BFTST (d16,An){offset:width} BFTST (d8,An,Xi){offset:width} BFTST (bd,An,Xi){offset:width} BFTST ([bd,An,Xi]){offset:width} BFTST ([bd,An,Xi],od.W){offset:width} BFTST ([bd,An,Xi],od.L){offset:width} BFTST ([bd,An],Xi){offset:width} BFTST ([bd,An],Xi,od.W){offset:width} BFTST ([bd,An],Xi,od.L){offset:width} BFTST (bd,An){offset:width} BFTST ([bd,An]){offset:width} BFTST ([bd,An],od.W){offset:width} BFTST ([bd,An],od.L){offset:width}
BFTST (xxx).W{offset:width} BFTST (xxx).L{offset:width}
BFTST (d16,PC){offset:width} BFTST (d8,PC,Xi){offset:width} BFTST (bd,PC,Xi){offset:width} BFTST ([bd,PC,Xi]){offset:width} BFTST ([bd,PC,Xi],od.W){offset:width} BFTST ([bd,PC,Xi],od.L){offset:width} BFTST ([bd,PC],Xi){offset:width} BFTST ([bd,PC],Xi,od.W){offset:width} BFTST ([bd,PC],Xi,od.L){offset:width} BFTST (bd,PC){offset:width} BFTST ([bd,PC]){offset:width} BFTST ([bd,PC],od.W){offset:width} BFTST ([bd,PC],od.L){offset:width}
ビットフィールドの内容に応じてフラグが変化する。詳細は後述。
MOVE.L #$12345678,D0 BFTST D0{28:16}
#$8123
をテストする
ビット操作命令と同様に、ビットフィールド命令はビットフィールドに対する操作を行う前(BFINS
は操作を行った後)にビットフィールドをテストし、その結果をフラグ(CCR
; コンディション・コード・レジスタの下位 5 ビット)に反映する。ビット操作命令は操作の対象が 1 ビットだけだったので Z
フラグだけが変化することになっていたが、ビットフィールド命令では、N
フラグと Z
フラグがテストの結果を反映し、V
フラグと C
フラグは常にクリアされる。X
フラグは変化しない。
フラグ | ビットフィールド命令のフラグ変化 |
---|---|
X | 変化しない |
N | 操作前(BFINS は操作後)のビットフィールドの最上位ビットの内容がコピーされる |
Z | 操作前(BFINS は操作後)のビットフィールドの全てのビットが 0 ならばセット、それ以外はクリア |
V | 常にクリア |
C | 常にクリア |
https://stdkmd.net/bitfield/ で再公開しました。図を SVG で描き直しました。
── 続きを読む ──── 続きを隠す ──
http://stdkmd.com/bitfield/ に引っ越しました。
BFINS のアドレッシングモードの説明で欠落していたソースオペランドを追加しました。
HTML を更新しました。
『M68000 ファミリのビットフィールド命令』と改題し、HTML 化して STUDIO KAMADA で公開しました。
月刊電脳倶楽部 138 号(1999 年 11 月号)の読み物横町のコーナーに『X680x0 アセンブラ講座 #$11《ビットフィールド命令》』として収録しました。