こんにちは、めのんです!
ゴールデンウィークなのでちょっとペースがズレちゃいましたけど、また少しずつ進めていくことにします。
今回は前回の続きで、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個が埋まりました。
それでは次回またお会いしましょう!