僕は最初、Rustで書こうとしたが所有権のあれこれをクリアできず挫折した。
メモリをRc<RefCell<Memory>
とかでごまかそうとしたけど、さすがにあかんやろて思った。
Rc<RefCell<>>
のオーバーヘッドがどれくらいなのか知らんけど、メモリアクセス毎にmemory.borrow()[addr];borrow_mut()[addr] += 1
ではむりがあるよなぁと詰んでた。
struct MBC { ram: RefCell<Vec<u8>> } struct CPU { mbc: Rc<MBC> } struct PPU { mbc: Rc<MBC> }
こんなかんじで、CPUとPPUからメモリにアクセスしたいと考えていたので構造体に組み込んでいたわけだけど、これじゃだめで
impl Gameboy { mbc: MBC; cpu: CPU; ppu: PPU; fn exec(&self) { self.cpu.exec(&mut self.mbc); self.ppu.exec(&mut self.mbc); } }
こんなかんじになるのかもしれない。不要な時は無駄に引き回したりせずに、必要な時に必要な権限を与えてあげるということを徹底するといいのかもしれない。俺はこの程度のこともわからんかったんだな。。。結局Typescriptで書いたGBEは上側の構造体になっている。
とかなんとか思ってたけど、上から渡す感じじゃなくて、下から引っ張る感じで所有してるのかな。両方できそうだが、僕ではわからん。なんでInnerで分ける必要があるのだろうか。
//context.rs Self { cpu: crate::cpu::Cpu::new(), inner: InnerContext0 { bus, inner: InnerContext1 { rom, ppu: crate::ppu::Ppu::new(dmg_palette), apu: crate::apu::Apu::new(), inner: InnerContext2 { model, running_mode, vram: vec![0; vram_size], vram_lock: false, oam: vec![0; 0xA0], oam_lock: false, external_ram, interrupt_enable: 0, interrupt_flag: 0, stall_cpu: 0, wake: false, }, }, }, //gameboy.rs pub fn exec_frame(&mut self) { use context::*; self.ctx.apu_mut().audio_buffer_mut().buf.clear(); let start_frame = self.ctx.inner.inner.ppu.frame(); while start_frame == self.ctx.inner.inner.ppu.frame() { self.ctx.cpu.step(&mut self.ctx.inner); } }
CPUのマクロ](https://github.com/tanakh/tgbr/blob/master/tgbr-core/src/cpu.rs)は狂気じみてるが、しこしこ手で書いていた僕も書きながらマクロでなんとかなりそうだなとか思いながらTS書いてたな。ここまでショートカットできるなんて思いもしなかったので怖い。