こんにちは、めのんです!
本当は前回のCPSE命令に続いてスキップ命令を一気にやってしまおうと思ったんですけど、I/Oレジスタを扱う命令がありますので先にIN命令とOUT命令を実装することにしました。
IN命令の実装
まずはIN命令です。
AVRには64本のI/Oレジスタがあります。
これらはその名の通りマイコンの外部に対する入出力に使うものですが、この命令セットシミュレータではいったん単なるメモリとして扱うことにします。
IN命令はその名の通り入力のための命令です。
指定したI/Oレジスタから指定した汎用レジスタに1バイト読み込みます。
それでは、いつものように「AVR®命令一式手引書」からIN命令の説明を引用しましょう。
I/Oレジスタを使うことを除けば、これまでの命令ととくに変わりはないと思います。
それではコードを見ていきます。
static void in(atmega328_t *cpu, uint16_t op)
{
int r = (op >> 4) & 0x1f;
int A = (op & 0xf) | (op >> 5) & 0x30;
cpu->r[r] = cpu->io[A];
++cpu->clock;
}
最初に汎用レジスタの番号rとI/Oレジスタの番号Aを取り出しています。
ビット演算はちょっと複雑ですが、命令の説明を見ながら地道に実装していきます。
そして、r番目の汎用レジスタにA番目のI/Oレジスタの値を代入しています。
今回はI/Oレジスタを単なるメモリとして扱っていますが、本当に外部から読み込むようにする場合はこの部分に適切に処理を追加してあげることになります。
最後はいつもクロックカウンタのインクリメントですね。
OUT命令の実装
次はIN命令の逆でOUT命令です。
指定したI/Oレジスタへ指定した汎用レジスタの値を1バイト書き込みます。
では、いつものように命令の説明を「AVR®命令一式手引書」から引用します。
ほとんどIN命令と同じですね。
コードも見てみましょう。
static void out(atmega328_t *cpu, uint16_t op)
{
int r = (op >> 4) & 0x1f;
int A = (op & 0xf) | (op >> 5) & 0x30;
cpu->io[A] = cpu->r[r];
++cpu->clock;
}
5行目が逆になっているだけなので説明は不要でしょう。
op_tableへの登録
次に、いつものようにop_tableに登録します。
opcode.phpに次のコードを追加して実行してあげればOKです。
for ($r = 0; $r < 32; ++$r)
{
for ($A = 0; $A < 64; ++$A)
{
$opcode_table[0b1011_0000_0000_0000 | ($r << 4) | ($A & 0x30) << 5 | ($A & 0xf)] = 'in';
$opcode_table[0b1011_1000_0000_0000 | ($r << 4) | ($A & 0x30) << 5 | ($A & 0xf)] = 'out';
}
}
今回は65,536命令のうち4,096個が埋まりましたね。
それでは次回またお会いしましょう!