こんにちは、めのんです!
今回は前回に引き続きビット単位の演算の一種であるAND命令とANDI命令を実装します。
AND命令の実装
まずはAND命令からです。
ビット単位のANDについてはとくに説明は必要ないと思います。
それではいつものように「AVR®命令一式手引書」から命令の説明を引用します。
ややこしいステータスレジスタの扱いは前回のEOR命令と同じです。
static void and(atmega328_t *cpu, uint16_t op)
{
int d = (op >> 4) & 0x1f;
int r = (op & 0xf) | (op >> 5) & 0x10;
int value = cpu->r[d] & cpu->r[r];
cpu->r[d] = value;
int sr = cpu->sr & ~(flag_S | flag_V | flag_N | flag_Z);
if (value & 0x80)
sr |= flag_N | flag_S;
if (value == 0)
sr |= flag_Z;
cpu->sr = sr;
++cpu->clock;
}
こちらもeor関数とほとんど同じですよね。
ひとつだけ注意しないといけないのは、「and」というのは<iso646.h>で定義されるマクロと同名です。
無いとは思いますが、<iso646.h>をインクルードしていると使えません。
ANDI命令の実装
次はANDI命令です。
AND命令は汎用レジスタどうしの演算でしたが、ANDI命令ではr16からr31の汎用レジスタとイミディエイト値の演算を行います。
それ以外はAND命令と同じです。
こちらも「AVR®命令一式手引書」から命令の説明を引用します。
汎用レジスタとして指定できるのはr16からr31で、r0らr15は指定できないことに要注意です。
あとはとくに目新しい内容はありません。
それではコードを見ていきます。
static void andi(atmega328_t *cpu, uint16_t op)
{
int d = 16 + ((op >> 4) & 0xf);
int K = (op & 0xf) | (op >> 4) & 0xf0;
int value = cpu->r[d] & K;
cpu->r[d] = value;
int sr = cpu->sr & ~(flag_S | flag_V | flag_N | flag_Z);
if (value & 0x80)
sr |= flag_N | flag_S;
if (value == 0)
sr |= flag_Z;
cpu->sr = sr;
++cpu->clock;
}
こちらもワンパターンですね。
ステータスレジスタの扱いは、そこそこの複雑さがあるのでインライン関数にでもまとめた方がいいのかもしれませんが、とりあえずこのまま進めます。
データシートの記述と付き合わせるには展開しておいた方が便利ですからね。
op_tableへの登録
次に、いつものようにop_tableに登録します。
opcode.phpに次のコードを追加して実行してあげればOKです。
for ($i = 0; $i < 1024; ++$i)
{
$opcode_table[0b0010_0000_0000_0000 | $i] = 'and';
}
for ($i = 0; $i < 4096; ++$i)
{
$opcode_table[0b0111_0000_0000_0000 | $i] = 'andi';
}
今回は65,536命令のうち 1,024 + 4,096 = 5,120 個が埋まりました。
それでは次回またお会いしましょう!