プログラミングや低レイヤで遊ぶ人

基本的には遊んだことを記事にしていく

ファミコンエミュレータ作り その1

golangによるファミコンエミュレータ作り

完成までのマラソン記事。がんばってやり切るぞい

また、注意点としてこの記事はコードを更新しながら作成していっていくので記事で書いてある内容と実際のコードに差が生まれる可能性はあります。

コード本体         

この記事の内容と最終目標

この記事の内容

ファミコンエミュレータを作成していく過程を記事にしていきます。基本的にはコードの説明なので、コードを眺めながらこの記事を読むのがベストです。

最終目標

本当はマッパー0のカセットが実行できるくらいまで実装するとかなり画面映するのですが、そこまでやると後半は細かい昨日をひたすら足していくだけのつまらない内容になるのでこの記事群での最終目標は下の図のような画面を表示することを目標とします。なお、申し訳ないのですがさすがに著作権的にあれな気がするのでこのromイメージはgithubには上げていません。 f:id:jumdtw:20200506193714j:plain ちなみに、この美少女は「アメイジンググレイス」というゲームのヒロインの一人です。とても面白いゲームなのでおすすめ。
アメイジンググレイスHP

環境

go version go1.13.8 windows/amd64

上記の環境で基本的にはやっていきます。windows以外での動作確認は基本しません。

golangを使う理由が特にあるわけではありません。ただのノリです。(rustやjavascriptで実装している人もいるらしい)

6052cpuの実装

現状のファイル構成はこんな感じです。

C:.
├─FC_CPU
|   ├─emu_CPU.go
|   └─opcmd.go
|
└─FC_APU

ここではCPUのエミュレータの本体を作っていきます。具体的には下記コードのような感じです。

const (
    memCap = 65535
)

type emu struct {
    // A X Y S P
    regi map[string]uint8
    // PC
    regPc uint16
    // memory
    memory [memCap]uint8
}

構造体を使用してエミュレータ本体を作成していきます。 ファミコンレジスタはPCは16bitで、それ以外は8bitです。 また、本来のファミコンのメモリ容量は2kBですがカセットのコード部分は0x8000部分に入ったりと2kBのまま作るとあとあと面倒臭そうだったので0xffffの容量で作成しました。

このCPUエミュレータは下記のコードで命令を実装します。

func (fcEmu *emu) Execute() {
    var opcd uint8 = fcEmu.memory[fcEmu.regPc]
    fcEmu.regPc++
    switch opcd {
    // lda
    case LDAIMM:
        fcEmu.ldaImm()
    case LDAZERO:
        fcEmu.ldaZero()
    case LDAZEROX:
        fcEmu.ldaZeroX()
    case LDAABS:
        fcEmu.ldaAbs()
    case LDAABSX:
        fcEmu.ldaAbsX()
    case LDAABSY:
        fcEmu.ldaAbsY()
    case LDAINDX:
        fcEmu.ldaIndX()
        .
        .
        .

このExecute()をmain文で実行していきます。

func main() {
    var fcEmu = emu{}
    _ = initEmu(&fcEmu)
    fcEmu.Execute()
    debug(&fcEmu)
}

まとめ

今回は単純なCPUエミュレートのため解説は少なめです。おそらく今回の部分はこの記事を読むより実際のコードを見た方が理解しやすいです。そのうち段々とファミコンの仕様を解説しつつコードの説明をしていきます。