3 ms·
Shameless plug - for anyone that's interested, I also wrote a NES emulator in Rust a few years back: https://github.com/koute/pinky https://github.com/koute/pin
by kouteiheika 8y ago
Shameless plug - for anyone that's interested, I also wrote a NES emulator in Rust a few years back: https://github.com/koute/pinky https://github.com/koute/pinky
I even compiled it to WebAssembly: https://koute.github.io/pinky-web/ https://koute.github.io/pinky-web/
> The CPU address space has several PPU registers mapped. So the CPU maintains a permanent mutable reference to the PPU. But the top-level Nes object also owns the PPU. I worked around this using Box to assign fixed memory addresses to values, and then “unsafe” pointer dereferences when needed.
There's actually a neat little trick which I used in my emulator which allows you to fulfill Rust's single ownership rule while simultaneously keeping the subcomponents of the emulator independent, making it possible to interconnect them without any unsafe and letting the compiler to optimize the whole thing as everything is statically dispatched.
Basically simplifying a little bit for each component (CPU, PPU, APU, etc.):
1. You put the whole state of the component into a separate `State` structure.
2. You create a `Context` trait which has a `get_state`/`get_state_mut` method as well as various callbacks needed by the component itself (e.g. PPU's `Context` has a `peek_video_memory` so that it can read video memory which is stored externally).
3. You create an `Interface` trait (with `Context` as supertrait) which contains the public interface of a given component (e.g. PPU has `execute`, `peek_ppustatus`, etc.).
4. You put the whole implementation of the component inside of a `Private` trait (with `Context` as supertrait).
5. You do a blanket impl of `Interface` and `Private` for any `T` which implements `Context`.
So then you create a single `Nes` structure which has `cpu::State`, `ppu::State`, etc. inside of it as fields, and implements the `cpu::Context`, `ppu::Context`, etc. traits. Inside of those impls you just wire the various subcomponents together, and voila, every component is encapsulated from each other and yet they can talk with each other, and there is no indirection anywhere (no `Box`es, no `RefCell`s, etc.).
- hathawsh 8y agoI'm really enjoying your 6502 emulation code. It feels clean and easy to read. https://github.com/koute/pinky/blob/master/mos6502/src/virtual_mos6502.rs https://github.com/koute/pinky/blob/master/mos6502/src/virtu... (I imagine the rest is good too, but 6502 is what I know. :-) )