ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

bit field

C のbit fieldの話です。

実際にどのようなコードになるかはコンパイラ依存になりますが、Cではビットフィールドが定義できます。教科書の例で行くと、

struct {
	unsigned int ready: 1;
	unsigned int enable: 1;
	unsigned int receivedByte: 8;
}receiver;

このように定義することで、readyに1bit、enableに1bit、receivedByteに8bit割り当てた構造体を定義できます。符号を考えたくないので、通常はunsigned intを使います。(ちなみにint を与えた場合は実装依存です。)

このように定義された構造体に対して操作を行うと、操作の対象を対応するbitのみにできます。実際に教科書の例を見てみましょう。

int data;
struct
{
	unsigned int ready: 1;
	unsigned int enable: 1;
	unsigned int receivedByte: 8;
}receiver;

int main(void){	
	receiver.receivedByte = 0xE1; /* 追加 */
	data = receiver.receivedByte;
	receiver.ready = 0;
	receiver.enable = 1;
}

receivedByteに何かしらの値を入れないと、何をしている分からないので、0xE1の代入をを追加しました。

gccコンパイルして、pcspimで動くようにしたアセンブラがこちらです。

	.text
	.align	2
	.set	nomips16
main:
	.frame	$fp,8,$31		# vars= 0, regs= 1/0, args= 0, gp= 0
	.mask	0x40000000,-8
	.fmask	0x00000000,0
	.set	noreorder
	.set	nomacro
	
	addiu	$sp,$sp,-8
	sw	$fp,0($sp)
	move	$fp,$sp
	lw	$2, receiver
	srl	$2,$2,22
	andi	$2,$2,0xff
	sw	$2, data
	lw	$3, receiver
	li	$2, 2147418112			# 0x7fff0000
	ori	$2, $2,0xffff
	and	$2, $3,$2
	sw	$2, receiver
	lw	$3, receiver
	li	$2,1073741824			# 0x40000000
	or	$2, $3,$2
	sw	$2, receiver
	move	$sp,$fp
	lw	$fp,0($sp)
	addiu	$sp,$sp,8
	j	$31
	nop

.data
data:	.word 0
.data
receiver:	 .word 0

pcspimで実行してみると、このような結果になります。

dataが0xE1になるのは良いのですが、receiverの所には0x78400000という見慣れないパターンが来ています。パタヘネのIn More Depth IMD 2.20-1を見るとビットフィールドとして、このように説明されています。

教科書通りの割り振りなら0xE1, 1, 0の順にビットが並んで0x00000386にならないといけません。なぜ同じにならないかというと、ビットフィールドと実際のメモリ上の割り付けがコンパイラによって違うからです。メモリ上の割り付けを規格で定めていないので、あるコンパイラは使用しているビットを右に詰め、別のコンパイラが違う割り付けをしても全く問題無いわけです。

0x78400000に関してはこの図を見てください。

gccは左(MSB)からビットを割り当てるため、この値になっています。