MOV命令とMOVW命令の実装

こんにちは、めのんです!

相変わらずのワンパターンですが淡々と実装を進めていきます。
今回はレジスタ間の転送命令を実装していきます。

MOV命令の実装

まずはMOV命令です。
これは8ビットの汎用レジスタの値を別の汎用レジスタにコピーします。

それで、いつものように「AVR®命令一式手引書」から命令の説明を引用します。

特別これといった説明はありません。

それではコードを見ていきます。

static void mov(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  int r = (op & 0xf) | (op >> 5) & 0x10;
  cpu->r[d] = cpu->r[r];
  ++cpu->clock;
}

転送元と転送先の汎用レジスタを指定するためのビットの扱いがちょっと面倒なことを除いて、何も目新しいことはないと思います。

MOVW命令の実装

次はMOVW命令です。
MOV命令は8ビット単位の転送でしたが、MOVW命令は2つの連続した汎用レジスタを16ビットのレジスタのように見立てて転送を行います。

こちらも「AVR®命令一式手引書」から命令の説明を引用します。

r4が分散していたMOV命令に比べてむしろ簡単ですね。

ひとつだけ注意しないといけないのは、r1からr4、d1からd4のようにr0やd0がないことです。
これは偶数番号のレジスタしか指定できないので、r0やd0は0に決まっているからです。

では、コードを見ていきます。

static void movw(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 3) & 0x1e;
  int r = (op << 1) & 0x1e;
  cpu->r[d] = cpu->r[r];
  cpu->r[d + 1] = cpu->r[r + 1];
  ++cpu->clock;
}

偶数番目の汎用レジスタと奇数番目の汎用レジスタをそれぞれ転送しているだけですね。

op_tableへの登録

次に、いつものようにop_tableに登録します。

opcode.phpに次のコードを追加して実行してあげればOKです。

for ($i = 0; $i < 0x400; ++$i)
{
  $opcode_table[0b0010_1100_0000_0000 | $i] = 'mov';
}

for ($i = 0; $i < 0x100; ++$i)
{
  $opcode_table[0b0000_0001_0000_0000 | $i] = 'movw';
}

今回は65,536命令のうち1,024 + 256 = 1,280個が埋まりました。

それでは次回またお会いしましょう!