ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

ゲームボーイを作る(9) - CPUのレジスタ実装

スポンサーリンク

ゲームボーイを作るその9。今回はテストの体裁を調えるために、CPU内部のレジスタを仮実装していく。

ゲームボーイのCPUのレジスタ実装

CPUのレジスタの仕様

まずは実装する必要のあるレジスタの仕様を把握しておこう。

Pan Docsレジスタ記載があるので、その表をとのまま拝借する。

16-bit Hi Lo Name/Function
AF A - Accumulator & Flags
BC B C BC
DE D E DE
HL H L HL
SP - - Stack Pointer
PC - - Program Counter/Pointer

ゲームボーイのCPUのレジスタは、命令によってBCのように16-bitのレジスタとして扱う場合と、B/Cのように8bitのレジスタとして扱う場合がある。 BC/DE/HLは汎用レジスタとなっており、特別な扱いを持たないレジスタA/FはName/Functionにあるとおりで、AccumlatorFlagsレジスタSP/PCはそのまんま、スタックポインタとプログラムカウンタのレジスタ

The Cycle-Accurate Game Boy Docsによると、これらCPUのレジスタの初期値は、ゲームボーイのモデルによって異なっている、、、らしい。 多分、レジスタの初期値に依存したコードはない(と信じている)が、もしそのようなケースに出くわした場合には、初期値を各々指定できるようにしないといけないかも。bgbはデバッガでみると、初期値が設定されているので、これに合わせておくのも1つの手かも。

フラグレジスタのみ、ビットフィールドの定義が別口になるので、そちらも確認しておく。N/H/C(BCD)は、これらのフラグビットがDAA命令の時にのみ使用されることを示している。

Bit Name Explanation
7 Z Zero Flag
6 N Add/Sub Flag (BCD)
5 H Half Carry Flag (BCD)
4 C Carry Flag (BCD)
3-0 - Not used (alwasys Zero)

ひとまず、テストベンチを作るに当たっては、レジスタの実体があれば良いのでそこだけ決めて作ってしまうことにした。

レジスタの実装

テスト環境の構築のための仮のレジスタを実装する。その7でIOだけ定義したCPUに、必要なレジスタを作った。多分、この後かなり実装が変化するとは思うが、ひとまず各レジスタをまとめて扱うためのトレイト/クラスを作ってある(BaseReg/GP//F/PC/CpuReg)。

/**
 * CPUレジスタのベースクラス
 *  - 処理をまとめるため、write/readのメソッドを宣言しておく
 */
trait BaseCpuReg extends Bundle {
  def write(data: UInt)
  def read(): UInt
}

/**
 * 汎用レジスタ用
 */
class GP(bits: Int) extends BaseCpuReg {
  val data = UInt(bits.W)

  def write(wr_val: UInt): Unit = data := wr_val
  def read(): UInt = data
}

/**
 * PC用
 *  - incメソッドを追加
 */
class PC(bits: Int) extends GP(bits) {
  def inc: Unit = data + 1.U
}

/**
 * Flagsレジスタ
 */
class F extends Bundle with BaseCpuReg {
  val z = Bool()
  val n = Bool()
  val h = Bool()
  val c = Bool()

  def write(data: UInt): Unit = {
    z := data(7)
    n := data(6)
    h := data(5)
    c := data(4)
  }
  def read(): UInt = Cat(z, n, h, c, 0.U(4.W))
}

/**
 * CPUレジスタをまとめたクラス
 */
class CpuReg extends Bundle {
  val a = new GP(8)
  val f = new F
  val b = new GP(8)
  val c = new GP(8)
  val d = new GP(8)
  val e = new GP(8)
  val h = new GP(8)
  val l = new GP(8)
  val sp = new GP(16)
  val pc = new PC(16)

  // for 16bit reg
  def af: UInt = Cat(a.read(), f.read())
  def bc: UInt = Cat(b.read(), c.read())
  def de: UInt = Cat(d.read(), e.read())
  def hl: UInt = Cat(h.read(), l.read())
}

class Cpu extends Module {
  val io = IO(new CpuIO)

  io := DontCare

  // declare registers
  val w_reg_init_val = WireInit(0.U.asTypeOf(new CpuReg))
  w_reg_init_val.pc := 0x100.U
  val r_regs = RegInit(w_reg_init_val)

  // increment PC.
  r_regs.pc.inc

  io.mem.addr := r_regs.pc
  io.mem.wen := false.B
}

とりあえず雑にレジスタは作ったので、次回はこれらのレジスタの値をチェックできるように、検証環境側を修正していく。