骨格の変更

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

今回はいつもとはちょっと違って、以前に扱った箇所の変更を扱います。
実装の初期に書いた「シミュレータの骨格」で扱った内容に変更を加えていくことにします。

変更の理由

単なるバグであれば過去の記事の訂正で済ませるのですけど、今回はちょっと変更が大きいのでひとつの記事にしたいと思います。
まずは今回こういう変更を行うことになった理由から説明していきます。

AVRの命令は、1ワードまたは2ワードの可変長です。
どの命令が1ワードで、どの命令が2ワードかはオペコードからは簡単にはわかりません。

最初はそれでもいいかなと思っていたんですけど、どうもそれではうまくいかないケースがあることに気付いたんです。
そういうこともあって、事前に次の命令が何ワードかを知る必要が出てきました。

具体的な実装変更

そういう事情なので、既存コードに対して思い切った修正を加えていくことにします。
といっても、できるだけ変更は最小限に抑えたいので、その方向で進めていきます。

opcode.phpの変更

個別の命令ごとにワード長を設定する必要がありますので、大方針としてopcode.phpを改造することで対応しようと思います。

以前のopcode.phpは標準出力に結果を出力していましたが、出力した内容はopcode.incにリダイレクトすることが決まっていますので、直接ファイルに書き出すことにします。
こうすることで、複数のファイルに出し分けることができるようになります。

opcode.incのほかに、ワード長の表をlength.incに出力できるようにしたいと思います。
length.incに出力する内容ですけど、65,536要素の配列を並べることにして、各要素は対応する命令のワード長とします。

ほとんどの命令のワード長は1ですので、そうではないところだけ2にできるようにします。
$opcode_tableの要素には通常関数名の文字列を格納しますが、ワード長が2の場合だけ配列を書き込むことにしましょう。
配列には関数名とワード長の2要素を格納します。

これまで登場した命令のうち、唯一の2ワード命令だったJMP命令の登録コードを次のように変更します。

for ($k = 0; $k < 64; ++$k)
{
  $opcode_table[0b1001_0100_0000_1100 | ($k & 0b11_1110) << 3 | ($k & 0b0001)] = [ 'jmp', 2 ];
}

そして、実際にファイルを出力する最後の部分を次のように変更します。

// できあがった配列を出力
$opcodes = '';
$lengths = '';
for ($i = 0; $i < 0x10000; $i++)
{
  $item = $opcode_table[$i];
  if (is_array($item))
  {
    $op = $item[0];
    $len = $item[1];
  }
  else
  {
    $op = $item;
    $len = 1;
  }
  $opcodes .= "$op,\n";
  $lengths .= "$len,\n";
}
file_put_contents('opcode.inc', $opcodes);
file_put_contents('length.inc', $lengths);

これでopcode.incとlength.incが出力されるようになりました。

atmega328.cの変更

opcode.incは今まで通りの内容ですので、それを使うdecode関数も今まで通りでかまいません。

今回新たに生成するようにしたlength.incを取り込んで命令の1ワード目のオペコードを添数として指定すればワード長を引き当てられる関数「length」を作ることにします。

static int length(uint16_t op)
{
  static uint8_t const length_table[0x10000] =
  {
#include "length.inc"
  };
  return length_table[op];
}

これで今回の目的であるワード長を取得できるようになりました。

今回作った関数「length」を具体的にどう使うかは次回以降でご紹介することにします。
どうぞご期待ください!