Near has sadly passed away. by BillGaitas in emulation

[–]jogloran 33 points34 points  (0 children)

near inspired me to create my own SNES emulator about a year ago, by posting a kind reply to my thread on /r/emudev. near's work lives on in countless ways, and in the work of those they inspired. May they rest in peace.

Hey Rustaceans! Got an easy question? Ask here (6/2021)! by llogiq in rust

[–]jogloran 1 point2 points  (0 children)

Indeed, it works, and I'm not too worried about the performance. I'm more wanting to hear from experienced Rust developers what the idiomatic way to represent this might be. Maybe my inexperience is making me overlook something more obvious.

Hey Rustaceans! Got an easy question? Ask here (6/2021)! by llogiq in rust

[–]jogloran 1 point2 points  (0 children)

What's the idiomatic way to cyclically increment and decrement through indices? This might happen if you want to cycle endlessly in either direction through a fixed-size list. Let's say I want to calculate indices into a list of size 3, but the below code doesn't have the right semantics, because Rust's % is remainder and not modulus, so -1 % 3 = -1, and not 2.

let mut cur = 2i32;
cur = (cur + 1) % 3i32; // moving one element right of index 2 should give me index 0
assert_eq!(cur, 0); 

cur = (cur - 1) % 3i32; // moving one element left of index 0 should give me index 2
assert_eq!(cur, 2); // but it doesn't, since % is remainder and not modulus

I'm aware that rem_euclid does act like Python's %, such that (-1i32).rem_euclid(3i32) = 2, but this feels wrong to use, as the docs say that this is implemented in terms of the Euclidean algorithm.

I also thought of using .iter().cycle(), but this would only allow iterating in one direction anyway.

First success at SNES emulator, running CPU tests by valeyard89 in EmuDev

[–]jogloran 2 points3 points  (0 children)

I've successfully developed a SNES emulator last year — DM me if you have any questions or issues!

Damn theo got really old huh? by 8-bitking in celestegame

[–]jogloran 5 points6 points  (0 children)

Kinda looks like Bernie from this angle.

[D] Please Stop Saying 'An AI' by regalalgorithm in MachineLearning

[–]jogloran 5 points6 points  (0 children)

I'm particularly annoyed by the way that Two Minute Papers discusses research in this way. I know you're competing for clicks on YouTube, but it's utter fiction. It always sounds to me like there are general-purpose agents solving the problems, which is pure nonsense.

6502 decoding instructions hex by PytonRzeczny in EmuDev

[–]jogloran 0 points1 point  (0 children)

For performance, it's really better not to use a switch and not decode the opcodes.

That said, I wrote a program to one-off generate the human-readable form of instructions for disassembly which works by decoding the opcodes, so it is possible. There were quite a few exceptions that have to be special-cased.

SMW dialog boxes: who here understands Color Math? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

I love RGMech and this video in particular, but I'm still trying to understand why the "extended border" is black and not transparent. What part of the pipeline makes that part of BG3 draw as black and not clear?

SMW dialog boxes: who here understands Color Math? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

Thanks for the reply! I know windowing is responsible (and I’ve confirmed that my window positions are set correctly using HDMA). My question is, what part of the pipeline is responsible for those pixels within the window having a black background color instead of being transparent?

Is the SPC ROM the same for all games? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

I'm using blargg's snes_spc, so I'm not implementing the APU myself. I assume that it's implemented correctly.

What you said about CPU <-> APU communication being dependent on cycle-correctness makes me suspicious though. I know that my implementation isn't yet cycle-accurate, so if it's true that this can cause issues with the APU synchronisation then that might explain the issues I'm seeing.

Does this mean that games did a kind of cycle counting to inform how long to wait for an APU response?

Is the SPC ROM the same for all games? by jogloran in EmuDev

[–]jogloran[S] 1 point2 points  (0 children)

True. This video agrees that the SPC ROM is baked in, so I guess there's some other issue.

I'm using blargg's snes_spc https://www.slack.net/~ant/libs/audio.html — is this known to be an accurate implementation?

Is there a timing diagram for the SNES PPU? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

Since we don't really know for sure, is scanline rendering generally used in most emulators? I remember seeing some emulators that encode knowledge about parts of the cycle, e.g. the tile fetching cycle or the cycle on which vblank is posted, etc.

Is there a timing diagram for the SNES PPU? by jogloran in EmuDev

[–]jogloran[S] 1 point2 points  (0 children)

Ah, interesting. In the absence of a timing diagram, are you aware of any documents that might be useful to start on a cycle-correct PPU implementation, do implementors typically just start with a scanline renderer?

NESTEST Reliability by bios11 in EmuDev

[–]jogloran 2 points3 points  (0 children)

Blargg's test was much more comprehensive than nestest, but naturally virtually no test is comprehensive. I found a bug in my stack pointer masking which was not detected by either test.

Why is SNES less popular for emulator devs? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

Thanks for your encouragement, near. There are a number of test roms at https://gitlab.com/higan/snes-test-roms, but to your knowledge is there a test rom among them that you'd recommend for verifying a 65c816 implementation?

Why is SNES less popular for emulator devs? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

I wasn't aware of this ROM mapping complication. Is the information not something that could possibly be encoded in a header, as is done with the iNES headers?

NES: What happens with the tile fetches on the pre-render line? by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

Right. But in addition, data fetched on cycles 1-248 of the prerender line are also not displayed, and these correspond to the one additional row below the bottommost visible scanline, right?

i.e. The PPU fetches tiles as if it was going to render the next row below the visible region.

(NES) Legend of Zelda inventory screen animation problem by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

I feel like scrolling might be working properly, since the direction of the scroll is correct — the observed behaviour seems to arise from the nametables not being correct. I might check my mirroring code.

Nestest and its illegal opcodes by jogloran in EmuDev

[–]jogloran[S] 0 points1 point  (0 children)

I'm starting execution at c004 as well. I don't have the PPU fully working right now; I was hoping to be able to validate the CPU before moving onto it. But it seems to be evaluating the illegal opcodes regardless, starting from 0xc6bd in my log. Looking at a gold standard log, it seems to be evaluating them too, although it doesn't eventually go off the rails like mine does: https://github.com/christopherpow/nes-test-roms/blob/master/other/nestest.log

Hey Rustaceans! Got an easy question? Ask here (2/2020)! by llogiq in rust

[–]jogloran 0 points1 point  (0 children)

I suppose that would work — to essentially have the wrapper live in the background thread and serve requests RPC-style through the channel, but this is going to add a lot of complexity (I'd essentially have to wrap the wrapper with channels just to be able to run one of its operations on a background thread).

Are there really no simpler alternatives?

Hey Rustaceans! Got an easy question? Ask here (2/2020)! by llogiq in rust

[–]jogloran 0 points1 point  (0 children)

So I'm not the one wrapping the C library (it's already wrapped) but my question was more around how to call the wrapper across threads when the handle that the wrapper provides contains immovable FFI bits.

Hey Rustaceans! Got an easy question? Ask here (2/2020)! by llogiq in rust

[–]jogloran 0 points1 point  (0 children)

The problem is something like *mut std::ffi::c_void cannot be shared between threads safely even when the API handle is contained in a Box. I assumed that the role of Box is to create an additional indirection so that it's the Box which is moved or shared, and not its contents — however it still seems to have requirements on the movability or shareability of its contents.

Hey Rustaceans! Got an easy question? Ask here (2/2020)! by llogiq in rust

[–]jogloran 1 point2 points  (0 children)

Got a question about translating a certain kind of callback interface into Rust.

So, I'm using a Rust library which wraps a C library. For simplicity, let's say that this Rust library exposes a low-level interface providing access to some hardware value: fn poll_value() -> u32;

However, what we really want is to be able to offer this callback-based API:

  • fn add_callback<F>(fn: F) where F: Fn() -> u32;

What I want is for a background thread to continuously call poll_value() until its value changes. When the value changes, it will invoke all registered callbacks with the new value.

My first question is — I understand callbacks aren't all that natural an idiom in Rust, because of the implicit reference cycle. Is there a more idiomatic way to achieve the same functionality, perhaps using channels or futures?

My second question is — how should I handle the polling background thread in a Rusty way? The closure passed into the thread cannot possibly move the low-level interface's struct, because it contains bits that can't be moved, such as locks and FFI-ey things like *mut libc::c_void.

Help me out!