こんにちは、めのんです!
今回は、前回行った骨格の変更が必要になる命令を早速追加していくことにします。
CPSE命令の実装
今回実装するCPSE命令は、2本の汎用レジスタを比較して、保持している値が一致していれば直後の命令をスキップします。
この命令も一種の条件分岐ですが、ステータスレジスタを使いませんしずっとシンプルですね。
こういうスキップ命令を持っているアーキテクチャはそんなに多くないと思います。
いつものように「AVR®命令一式手引書」からCPSE命令の説明を引用しましょう。
今回は書式も実行周期数も3パターンあるのでちょっと複雑ですね。
でも、よく見ればそんなに大したことはありません。
比較する2本のレジスタの値が一致したとき、直後の命令が1ワード命令か2ワード命令かでスキップする(プログラムカウンタに加算する)アドレスが変わるだけです。
それではコードを見ていきます。
static void cpse(atmega328_t *cpu, uint16_t op)
{
int d = (op >> 4) & 0x1f;
int r = (op & 0xf) | (op >> 1) & 0x10;
if (cpu->r[d] == cpu->r[r])
{
int l = length(cpu->program[cpu->pc]);
cpu->pc += l;
cpu->clock += 1 + l;
}
else
{
++cpu->clock;
}
}
r4が分散しているのでちょっと面倒なビット演算がありますが、それ以外はそんなに難しいコードはありません。
cpse関数が呼び出されるときには、fetch関数で既にプログラムカウンタがインクリメントされていますので、2本のレジスタの値が一致すれば1または2(length関数で取得)を加算します。
clockには2または3を加算する必要があります。
レジスタの値が一致しない場合は、プログラムカウンタはそのまま、clockには1を加算します。
op_tableへの登録
次に、いつものようにop_tableに登録します。
opcode.phpに次のコードを追加して実行してあげればOKです。
for ($d = 0; $d < 32; ++$d)
{
for ($r = 0; $r < 32; ++$r)
{
$opcode_table[0b0001_0000_0000_0000 | ($r & 0x10) << 5 | ($r & 0xf) | ($d & 0x1f) << 4] = 'cpse';
}
}
今回も65,536命令のうち1,024個が埋まりましたね。
それでは次回またお会いしましょう!