ゲームボーイを作るその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にあるとおりで、Accumlator
とFlags
レジスタ。
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.scala
/** * 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 }