LD(LDD)命令とST(STD)命令の実装

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

ゴールデンウィークなのでちょっとペースがズレちゃいましたけど、また少しずつ進めていくことにします。

今回は前回の続きで、Yレジスタ版とZレジスタ版のLD命令とST命令、そしてLDD命令とSTD命令を実装していきます。
LD命令とST命令についてはXレジスタ版とほとんど同じなんですけどね(笑)

LD(LDD)命令の実装

LD(LDD)命令と書きましたが、実際にはLD命令とLDD命令があります。
前回実装したLD命令とはアドレッシングモードが異なります。

具体的な説明に入る前に、いつものように「AVR®命令一式手引書」から命令の説明を引用します。
前回も多かったんですが、今回はもっとたくさんあります。

Yレジスタ版とZレジスタ版の2ページを引用しました。
レジスタが異なる点を除けば前回のXレジスタ版のLD命令と同じです。

今回新たに追加になったのはLDD命令です。
LDD命令はXまたはYレジスタにq0~q5で指定した値を加えたアドレスの値をロードします。

ところで、①のLD命令はq0~q5がすべて0の場合のLDD命令と同じなので、今回は②と③のLD命令とLDD命令を実装することにします。
LDD命令はCでいえば *(X + q) に相当します。

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

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

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

static void ldd_y(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  int q = (op & 0x7) | (op >> 7 & 0x18) | (op >> 8 & 0x20);
  cpu->r[d] = cpu->data[cpu->y + q];
  cpu->clock += 2;
}

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

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

static void ldd_z(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  int q = (op & 0x7) | (op >> 7 & 0x18) | (op >> 8 & 0x20);
  cpu->r[d] = cpu->data[cpu->z + q];
  cpu->clock += 2;
}

LD命令については、Xレジスタ版のものと区別するためにld_y_2のように_yまたは_zを添字にしています。
それ以外は取り立てて説明することもないでしょう。

ST(STD)命令の実装

こちらもYレジスタ版とZレジスタ版のST命令とSTD命令になります。
ST命令は前回実装したST命令とはアドレッシングモードが異なります。

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

それでは実装です。

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

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

static void std_y(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  int q = (op & 0x7) | (op >> 7 & 0x18) | (op >> 8 & 0x20);
  cpu->data[cpu->y + q] = cpu->r[d];
  cpu->clock += 2;
}

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

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

static void std_z(atmega328_t *cpu, uint16_t op)
{
  int d = (op >> 4) & 0x1f;
  int q = (op & 0x7) | (op >> 7 & 0x18) | (op >> 8 & 0x20);
  cpu->data[cpu->z + q] = cpu->r[d];
  cpu->clock += 2;
}

こちらもとくに説明する必要はないでしょう。

op_tableへの登録

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

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

for ($d = 0; $d < 32; ++$d)
{
  $opcode_table[0b1001_0000_0000_1001 | $d << 4] = 'ld_y_2';
  $opcode_table[0b1001_0000_0000_1010 | $d << 4] = 'ld_y_3';
  $opcode_table[0b1001_0010_0000_1001 | $d << 4] = 'st_y_2';
  $opcode_table[0b1001_0010_0000_1010 | $d << 4] = 'st_y_3';
  for ($q = 0; $q < 0x40; ++$q)
  {
    $t = ($q & 0x7) | ($q & 0x18) << 7 | ($q & 0x20) << 8;
    $opcode_table[0b1001_0000_0000_1010 | $d << 4 | $t] = 'ldd_y';
    $opcode_table[0b1000_0010_0000_1000 | $d << 4 | $t] = 'std_y';
  }
}

今回は65,536命令のうち32 × 4 + 2,048 × 2 = 4,224個が埋まりました。

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