今回は型宣言、拡張、プロトコル、そしてカートリッジ関係について簡単に解説しています。
最終的に .nes ファイルを読み込んでファイルの情報を表示できるようにします。
まずは typealias で Byte, Word, Address を定義しています。
オリジナルでは Byte, Word はどちらも Number になっていて 8bit や 16bit にはなっていません。それだと 0x00FF でマスクしたりして大変そうなので Swift ではそれぞれ UInt8, UInt16 にしてあります。ただ、Word, Byte それぞれへのキャストが増えたり、配列の添字が Int でないとダメなのでそこでもキャストが更に増えてしまい、結局良かったのかどうかは微妙です?
Address は必要ないですが Word より Address とした方が意味がわかりやすい部分に使いたかったので。
UInt16+Page.swift
UInt16 には extension で上位 8ビットを page、下位 8ビットを offset としてアクセスできるようにしてあります。毎回マスクしてシフトする手間が省けます。また、page と offset を入力して UInt16 も作れます。
let adr = Address(page: 0xFF, offset: 0xFC) // adr は 0xFFFC になる
UInt8+Bit.swift
UInt8 にも extension で添字を使ってビット情報を取得できるようにしてあります。CPU のフラグのチェックなどで (A & 0x04) != 0 など書く手間が省けます。
(P & 0x10) != 0 ? true : false // extension なし P[4] // extension あり
ちなみに UInt16+page.swift と UInt8+Bit.swift は他で見かけたコードの一部を持ってきています。
IO.swift
メモリのアクセス用に Read/Write それぞれのプロトコルを定義して、それぞれで Byte, Word のデータを読み書きができる様にしました。
ROM.swift
UInt8 の配列に Read プロトコル、そして配列のサイズを確認する size 関数のみです。
6502 は little endian なので offset 部分が小さいアドレス、page 部分が大きいアドレスに入っています。
func read(_ address: Address) -> Word { return Word(page: memory[Int(address &+ 1)], offset: memory[Int(address)]) }
RAM.swift
ROM に Write プロトコルを足したもの。
ここでようやく NES なお話ですが、カートリッジ(.nes ファイル)の詳細はココにある通りです。
日本語ではオリジナルの qiita の記事のカセットの部分に詳しく書いてあります。
Cartridge.swift
Carridge ROM データを Parser がパースした時に戻り値として使うデータの struct です。内容はキャラクタデータ、プログラムデータ、そしてプログラムが Horizontal Mirror なのか Virtical Mirror なのかを示すフラグの isHorizontalMirror、最後にこれはオリジナルにはないですが Mapper の番号を示す mapper です。
extension の拡張部分は print した時に少し見やすくするためです。
Parser.swift
Cartridge ROM データ を Struct Cartridge にパースします。
上記ページに詳細は書いてありますが簡単に説明すると。
- 0x0004 プログラムデータのページサイズ
- 0x0005 キャラクタデータのページサイズ
- 0x0006 bit0 縦横ミラーのフラグ
となっていて、それぞれのサイズを使って Cartridge ROM データ(.nes ファイル)からキャラクタ/プログラム ROM オブジェクトを切り出します。
また 0x0006 の上位4bit を下位4bit、0x0007 の上位4bit を上位4bit とした値はマッパーの番号になります。
これらの情報を使って struct Cartridge を作って返します。
今回のコードを実行できる形にした Xcode のプロジェクトを github にアップロードしました。
実行して適当な .nes ファイル(例えばココのgiko005.nes)を読み込むと、そのファイルの Cartridge 情報をログに表示します。
Cartridge: isHorizontalMirror: false characterROM: 8192 programROM: 16384 mapper: 0
GitHub
StNesEmu