2020年12月第4週レポート
インプット
📝 「自作エミュレータで学ぶx86アーキテクチャ」を読んだ
先週RustでChip-8を作ってみたところ、エミュレータ開発に興味が沸いたので読んでみました。
サンプルコードはCとアセンブラでしたが、Cで実装された部分についてはRustで実装してみました。Chip-8でCPUの動作イメージはなんとなく掴めていた(昔、CPUの創りかたを読んでいたのもよかったのかも)のか、割とスラスラと読めたと思います。言語入門書にあるようなコードがどう実行されるかは分かったけど、もっと具体的なアプリケーションの動作が見えてくれるようになるとおもしろいのかも。そういった意味ではファミコンアセンブラとかおもしろいかもしれません。ということで次はファミコンエミュレータを作ってみることにします。
📝 Rustのモジュールシステムについて調べた
Rustのモジュールシステムがよく分かってなかったので調べました。 特にサブモジュールを作ったときの可視性がよくわからなかったのですが、以下の記事がとても参考になりました。
📝 RustでVecを分割して2つのVecにする方法を調べた
ファイルからロードしたバイナリをあるところで分割してそれぞれ扱いたい、そんなことあると思います。TS/JSだとslice
でちょちょいのちょいですがRustだとどうすればいいんだろうかということで公式ドキュメントをみながら色々試したみた。
ベクタをスライスに変換して分割したあとにベクタに変換する
let vec = vec![1,2,3,4,5,6,7,8,9];
println!("vec size: {}, capacity: {}", vec.len(), vec.capacity());
let slice = &vec;
let sliced_vec1 = slice[..3].to_vec();
println!("vec1 {:?} size: {}, capacity: {}",sliced_vec1, sliced_vec1.len(), sliced_vec1.capacity());
let sliced_vec2 = slice[4..].to_vec();
println!("vec2 {:?} size: {}, capacity: {}",sliced_vec2, sliced_vec2.len(), sliced_vec2.capacity());
結果
vec size: 9, capacity: 9
vec1 [1, 2, 3] size: 3, capacity: 3
vec2 [5, 6, 7, 8, 9] size: 5, capacity: 5
to_vec()
で新しいベクタをアロケートするのでメモリ使用量は増えそう。分割元を捨てれば済む話ではあるので、そういった実装にできそうなら悪くない気がする。ただ、公式ドキュメントによるとアロケートは遅いらしい(Vecはヒープに値が作られるっぽいし)ので頻繁に使うようなところはスライスを使ったほうがいいのかもしれない。
Vec::split_off
let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
println!("vec size: {}, capacity: {}", vec.len(), vec.capacity());
let sliced_vec1 = vec.split_off(4);
println!("vec1 {:?} size: {}, capacity: {}", sliced_vec1, sliced_vec1.len(), sliced_vec1.capacity());
println!("vec {:?} size: {}, capacity: {}", vec, vec.len(), vec.capacity());
結果
vec size: 9, capacity: 9
vec1 [5, 6, 7, 8, 9] size: 5, capacity: 5
vec [1, 2, 3, 4] size: 4, capacity: 9
split_off
は引数で指定したインデックス以降の要素を別のベクタとしてアロケートするメソッド。インスタンスのベクタから切り出された要素は削除されるものの、キャパシティは変わらないので無駄にメモリを食わないように気をつけた方がいいらしい。
📝 Rustでバイナリ列を文字列に変換する方法を調べた
let str = String::from_utf8(data[..4].to_vec());
if let Err(e) = str {
// Error
}
String::from_utf8
にVec<u8>
をUTF-8な文字列に変換してくれる。戻り値はResult
型なのでエラーチェックかアンラップしてあげる必要がある
アウトプット
🛠️ Rustでファミコンエミュレータを作り始めた
所有権システムに翻弄されているので設計見直し。#NES pic.twitter.com/rga502Su6C
— d_yama (@dy_karous) December 28, 2020
ということで作り始めた。
レギュレーションは以下の通りに設定
- 既存コードは(できるだけ)見ない、参考にしない
- NESの仕様を調べるのはOK(Nes Dev wikiとか)
目的は以下の通りに設定
- Rustの記述力の向上
- 仕様理解力の向上
- プログラム設計力の向上
既存コードを読んでしまうと、作りたいものの仕様把握が疎かになってしまったり設計が既存コードに引っ張られてしまい能力向上に貢献しないので今回は模倣は禁止。仕様をどのように理解するかがプログラムのデザインに大きく反映されるのでここはサボらずやっていくのが目的に沿うのかなあと。
Mapper0、1、3(できれば4も)のROM対応と、音をちゃんと出す(たぶんこれが一番ハードル高くなりそう)のを目標にしようかなと思います。
その他
✨ Obsidianを使い始めたよ
新しい言語を使い始めたり、エミュレータ開発で調べたことをまとめたりとメモする機会が増えてきたので改めてメモツールを導入しようと思い立ちました。会社ではesaを使っているのですが、せっかくなので個人用でゆくゆくは公開したいので、そのあたりが容易にできそうなものを使いたいです。
最初はScrapboxを使ってみようと思ったのですが、いろいろ調べていく過程でObsidianという新興ツールが目に入ったのでしばらく使ってみようと思います。ドキュメントを読んでも使い方がわかるような?わからないような?なので、まずはデイリーメモを溜めていくかんじで取り組んでみようかと思っています。