Earlier quoted context omitted.
It doesn’t remove restrictions, it adds unchecked constructs. Very different!
Hm, I’m not sure I see the difference between those concepts, but I do like your framing better.
Implementing a NES Emulator in Rust
61–70 of 93 posts
Re: Implementing a NES Emulator in Rust
#62Re: Implementing a NES Emulator in Rust
#63I even compiled it to WebAssembly: 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.).
Re: Implementing a NES Emulator in Rust
#64Earlier quoted context omitted.
It doesn’t remove restrictions, it adds unchecked constructs. Very different!
Hm, I’m not sure I see the difference between those concepts, but I do like your framing better.
This matters because of stuff like the article; if you see the other thread, I’m pretty sure (though haven’t verified yet) that this code has UB, even though they’re using unsafe.
Re: Implementing a NES Emulator in Rust
#65What was the most challenging part for you in building this emulator?
The problem is I haven't really found any yet that helped me (admittedly, I have so far devoted just one Saturday morning to it.) Are there any specs about the hardware that you used, or that anyone else can share?
Re: Implementing a NES Emulator in Rust
#66Re: Implementing a NES Emulator in Rust
#67 for (idx, x) in xs.iter().enumerate()
instead of for (x, idx) in xs.iter().zip(0..xs.len())
you get the 4x unroll (https://godbolt.org/z/XDyx0w).Re: Implementing a NES Emulator in Rust
#68Earlier quoted context omitted.
OP is using unsafe to store a mut pointer to a boxed value so that it can be mutated in two places. So the statement is just referring to data races.
Sounds like UB to me! I’ll have to take a look. It really depends on the details. (I tried to run miri on it, but I'm on Windows, and this is unix-only.) EDIT: even with porting some of the code, I can’t get the current repository to build, there’s a borrow checker error...)
$ rustc --version
rustc 1.32.0 (9fda7c223 2019-01-16)
$ cargo --version
cargo 1.32.0 (8610973aa 2019-01-02)
$ cargo build --release
$ cargo run --release --bin nes-emulator
`Re: Implementing a NES Emulator in Rust
#69Earlier quoted context omitted.
Sounds like UB to me! I’ll have to take a look. It really depends on the details. (I tried to run miri on it, but I'm on Windows, and this is unix-only.) EDIT: even with porting some of the code, I can’t get the current repository to build, there’s a borrow checker error...)
Sorry about that. If it helps, I'm using Rust 1.32 to build: $ rustc --version rustc 1.32.0 (9fda7c223 2019-01-16) $ cargo --version cargo 1.32.0 (8610973aa 2019-01-02) $ cargo build --release $ cargo run --release --bin nes-emulator `
1. I can send you a patch later, but if you use std::io::stdin() and stdout, instead of opening the file descriptors, it should compile on Windows.
2. The borrow error was coming from miri; it detected some UB! Compiling with cargo does work. It’s not the bit I expected; I can file a bug with the error once I’m back at my laptop.
Anyway, this is a cool project, and I’m glad you’re doing it; don’t let my worries bug you. I’ve been trying to learn more unsafe stuff and so the UB bits are just on my mind more lately.
EDIT: actually it's just from building on nightly rust, not stable. No miri needed. I wonder why stable is okay with this... anyway I filed some github issues, please feel free to ignore me or close them, but if you wanna keep talking about this, let's do that there :)
Re: Implementing a NES Emulator in Rust
#70What was the most challenging part for you in building this emulator?
I want to build one myself as well, but I don't want to use existing code as a guide, rather challenge myself to implement from raw specs. The problem is I haven't really found any yet that helped me (admittedly, I have so far devoted just one Saturday morning to it.) Are there any specs about the hardware that you used, or that anyone else can share?
Everything you need is either on http://wiki.nesdev.com or on their forums. I know because that's what I did with my NES emulator - I explicitly didn't want to look at any source code and instead wanted to implement everything only based on the docs.
Granted, what's on the NESdev isn't always easy to grok, up to the point of being really confusing sometimes. What I've found really helped is gradually setting up a test suite based on various test ROMs I could find, which even allows you to implement some parts of the emulator TDD-style once you get the basics up and running.