LD命令とST命令の実装

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

今回はそこそこ大物を片付けていきます。
メモリから汎用レジスタへのロードと、逆に汎用レジスタからメモリへのストアをXレジスタを使って行う命令です。

Cのポインタは要するにこういうことなので、アセンブリ言語を先にやっておくとCをやったときにポインタで躓くことはなくなると思います。

LD命令の実装

LD命令はデータメモリから1バイトの値を読み込んで指定した汎用レジスタに設定します。
データメモリから読み込むアドレスはXレジスタで指定します。

XレジスタというのはR26とR27を使って16ビットの値を表現するレジスタでしたね。
今作っている命令セットシミュレータでは共用体を上手く使ってxというメンバでアクセスできるようにしています。

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

ご覧のように、LD命令といっても実際には3種類があります。
こんな風に、基本的には同じ命令でもオペランドの指定の仕方が違うものがあります。
このようなオペランドの指定の仕方を「アドレッシングモード」といいます。

①は一番単純なアドレッシングモードで、Xレジスタの値をそのままデータメモリのアドレスにします。

②もそうなのですが、あとでXレジスタをインクリメントします。
Cでいえばx++といったところですね。

③は先にXレジスタをデクリメントしてからデータメモリのアドレスにします。
Cでいえば–xにあたります。

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

static void ld_1(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  cpu->r[d] = cpu->data[cpu->x];
  cpu->clock += 2;
}

static void ld_2(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  cpu->r[d] = cpu->data[cpu->x++];
  cpu->clock += 2;
}

static void ld_3(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  cpu->r[d] = cpu->data[--cpu->x];
  cpu->clock += 2;
}

全部LD命令なので、3種類の関数に分けるために_1, _2, _3という添字を付けています。
この点だけは今までと違うので注意が必要です。

Xレジスタがデータメモリの範囲を超えた場合にどう振る舞うのかが「AVR®命令一式手引書」からは読み取れませんでした。
実機で確認して同じように振る舞わせることもできないわけではないのですけど、ここはいったん放置することにします。

ST命令の実装

ST命令はLD命令の逆で、指定した汎用レジスタの値をデータメモリに書き込みます。
データメモリのアドレスはLD命令同様Xレジスタで指定します。

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

ST命令もアドレッシングモード違いのものが3つありますね。
アドレッシングモードはLD命令と同じなので説明は省略します。

static void st_1(atmega328_t *cpu, uint16_t op)
{
  int r = (op >> 4) & 0x1f;
  cpu->data[cpu->x] = cpu->r[r];
  cpu->clock += 2;
}

static void st_2(atmega328_t *cpu, uint16_t op)
{
  int r = (op >> 4) & 0x1f;
  cpu->data[cpu->x++] = cpu->r[r];
  cpu->clock += 2;
}

static void st_3(atmega328_t *cpu, uint16_t op)
{
  int r = (op >> 4) & 0x1f;
  cpu->data[--cpu->x] = cpu->r[r];
  cpu->clock += 2;
}

LD命令とは代入演算子の両辺が逆になっているだけですね。

op_tableへの登録

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

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

for ($d = 0; $d < 32; ++$d)
{
  $opcode_table[0b1001_0000_0000_1100 | $d << 4] = 'ld_1';
  $opcode_table[0b1001_0000_0000_1101 | $d << 4] = 'ld_2';
  $opcode_table[0b1001_0000_0000_1110 | $d << 4] = 'ld_3';
  $opcode_table[0b1001_0010_0000_1100 | $d << 4] = 'st_1';
  $opcode_table[0b1001_0010_0000_1101 | $d << 4] = 'st_2';
  $opcode_table[0b1001_0010_0000_1110 | $d << 4] = 'st_3';
}

今回は65,536命令のうち32 × 6 = 192個が埋まりました。
実装量は結構あった気がしますが埋まった命令は意外に少ないですね。

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