Hacker News new | past | comments | ask | show | jobs | submit login

Cycle-perfect emulators at least for typical 8- and 16-bit systems need to run each emulator subsystem (CPU, video, audio, ...) in lock-step for each clock cycle. Spreading those emulator systems over multiple threads would mean that the threads need to synchronize with each other after each step (which is just a few host CPU instructions long). It might be easier for more modern hardware where subsystems are much more decoupled from each other (and I guess with Moore's Law dead this is our only hope to emulate more modern hardware in the future).



That's true. In the case of Nintendo DS, I'm aligning doing line-based emulation; so I first serially emulate the two CPUs (I could in theory parallelize then, but I haven't properly design the emulator around this, so all memory-mapped callbacks are not concurrent safe), and then emulate the graphic subsystem for a single line, spreading the different layers across different goroutines.

This means that goroutines synchronize 263*60 times per second, which is something. I measure a high contention, so I'm not even sure it was worth in the end, but I thought it was a good experiment to attempt :)

Another Go-related thing I did was using struct tags to configure memory-mapped registers in a declarative way. See for instance: https://github.com/rasky/ndsemu/blob/master/irq.go#L13

where I configure the memory-mapped registers of the interrupt controller; "wcb" / "rcb" tags stand for write/read callbacks, which means that specific callbacks (with names matching the register names) will be called for each read/write operation; this is all achieved through reflection. The same applies to mapping the whole register bank, which is done here: https://github.com/rasky/ndsemu/blob/master/nds9.go#L69

The memory subsystem handling these and more features is in emu/hwio, and I'm actually pretty proud of it. For the memory mapping itself, a radix tree is built which is quite quick to lookup and doesn't require much barcoding.

In fact, the whole "emu" package is a generic framework for emulators in Go, and I plan to reuse it in the future :)


The Octalyzer has separate threads for cpu, sound, memory, networking, graphics and the main opengl loop. We use vertical blanking and sound timing to keep it all in sync.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: