スキップ命令の実装

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

今回はスキップ命令を一気に実装してしまいます。
AVRには5つのスキップ命令があるんですけど、そのうちCPSE命令はすでに実装済みですので、今回は残りの4つを実装します。

SBRS命令とSBRC命令の実装

まずはSBRS命令とSBRC命令を実装します。
これらの命令の実装はほとんど変わりませんのでまとめて扱いますね。

それでは、いつものように「AVR®命令一式手引書」から各命令の説明を引用しましょう。

これからの命令では指定した汎用レジスタの指定したビットを調べます。
SBRS命令はそのビットが1のときに次の命令をスキップします。
SBRC命令は逆でビットが0のときに次の命令をスキップします。

スキップについてはCPSE命令を実装したときに説明しました

実際のコードは次のようになります。

static void sbrs(atmega328_t *cpu, uint16_t op)
{
  int r = (op >> 4) & 0x5;
  int b = op & 0x7;
  if (cpu->r[r] & (1 << b))
  {
    int l = length(cpu->program[cpu->pc]);
    cpu->pc += l;
    cpu->clock += 1 + l;
  }
  else
  {
    ++cpu->clock;
  }
}

static void sbrc(atmega328_t *cpu, uint16_t op)
{
  int r = (op >> 4) & 0x5;
  int b = op & 0x7;
  if (!(cpu->r[r] & (1 << b)))
  {
    int l = length(cpu->program[cpu->pc]);
    cpu->pc += l;
    cpu->clock += 1 + l;
  }
  else
  {
    ++cpu->clock;
  }
}

2つの命令の違いは、5行目と21行目にあるif文の条件式だけですね。
これだけ共通部分が多いので、下請け関数を作るなりマクロを作るなりしてもいいのでしょうけど、まあこの程度なので愚直に2改革ことにしました。

SBIS命令とSBIC命令の実装

次はSBIS命令とSBIC命令です。

先ほどのSBRS命令とSBRC命令は汎用レジスタのビットを判断するものでしたけど、SBIS命令とSBIC命令はI/Oレジスタのビットを判断してスキップします。

命令の説明は次のようになっています。

オペコード中に埋め込まれたオペランドの配置がかなり違っています。
でも、動作自体はほとんど同じです。

static void sbis(atmega328_t *cpu, uint16_t op)
{
  int A = (op >> 3) & 0x1f;
  int b = op & 0x7;
  if (cpu->io[A] & (1 << b))
  {
    int l = length(cpu->program[cpu->pc]);
    cpu->pc += l;
    cpu->clock += 1 + l;
  }
  else
  {
    ++cpu->clock;
  }
}

static void sbic(atmega328_t *cpu, uint16_t op)
{
  int A = (op >> 3) & 0x1f;
  int b = op & 0x7;
  if (!(cpu->io[A] & (1 << b)))
  {
    int l = length(cpu->program[cpu->pc]);
    cpu->pc += l;
    cpu->clock += 1 + l;
  }
  else
  {
    ++cpu->clock;
  }
}

op_tableへの登録

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

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

for ($r = 0; $r < 32; ++$r)
{
  for ($b = 0; $b < 8; ++$b)
  {
    $opcode_table[0b1111_1110_0000_0000 | ($r << 4) | $b] = 'sbrs';
    $opcode_table[0b1111_1100_0000_0000 | ($r << 4) | $b] = 'sbrc';
  }
}

for ($A = 0; $A < 32; ++$A)
{
  for ($b = 0; $b < 8; ++$b)
  {
    $opcode_table[0b1001_1011_0000_0000 | ($A << 3) | $b] = 'sbis';
    $opcode_table[0b1001_1001_0000_0000 | ($A << 3) | $b] = 'sbic';
  }
}

今回は65,536命令のうち1,024個が埋まりましたね。

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