All Projects → cronokirby → Ludus

cronokirby / Ludus

Licence: mit
A pluggable NES emulator

Programming Languages

rust
11053 projects

Labels

Projects that are alternatives of or similar to Ludus

Provenance
iOS & tvOS multi-emulator frontend, supporting various Atari, Bandai, NEC, Nintendo, Sega, SNK and Sony console systems… Get Started: https://wiki.provenance-emu.com |
Stars: ✭ 4,732 (+29475%)
Mutual labels:  emulation
Speakeasy
Windows kernel and user mode emulation.
Stars: ✭ 605 (+3681.25%)
Mutual labels:  emulation
Shadow
Shadow is a unique discrete-event network simulator that runs real applications like Tor, and distributed systems of thousands of nodes on a single machine. Shadow combines the accuracy of emulation with the efficiency and control of simulation, achieving the best of both approaches.
Stars: ✭ 769 (+4706.25%)
Mutual labels:  emulation
Xqemu
Open-source emulator to play original Xbox games on Windows, macOS, and Linux
Stars: ✭ 518 (+3137.5%)
Mutual labels:  emulation
Jsnes
A JavaScript NES emulator.
Stars: ✭ 5,354 (+33362.5%)
Mutual labels:  emulation
Vivisect
Stars: ✭ 672 (+4100%)
Mutual labels:  emulation
Higan
higan is a multi-system emulator focused on accuracy, preservation, and configurability.
Stars: ✭ 457 (+2756.25%)
Mutual labels:  emulation
Little Virtual Computer
Learn how computers work by simulating them in Javascript
Stars: ✭ 819 (+5018.75%)
Mutual labels:  emulation
Cemu
Third-party TI-84 Plus CE / TI-83 Premium CE emulator, focused on developer features
Stars: ✭ 593 (+3606.25%)
Mutual labels:  emulation
Vigembus
Windows kernel-mode driver emulating well-known USB game controllers.
Stars: ✭ 721 (+4406.25%)
Mutual labels:  emulation
Xenia
Xbox 360 Emulator Research Project
Stars: ✭ 5,404 (+33675%)
Mutual labels:  emulation
Pcsx2
PCSX2 - The Playstation 2 Emulator
Stars: ✭ 5,830 (+36337.5%)
Mutual labels:  emulation
Skyline
Run Nintendo Switch homebrew & games on your Android device!
Stars: ✭ 670 (+4087.5%)
Mutual labels:  emulation
Flare Emu
Stars: ✭ 487 (+2943.75%)
Mutual labels:  emulation
Citra
A Nintendo 3DS Emulator
Stars: ✭ 7,009 (+43706.25%)
Mutual labels:  emulation
Dynarmic
An ARM dynamic recompiler.
Stars: ✭ 475 (+2868.75%)
Mutual labels:  emulation
Ryujinx
Experimental Nintendo Switch Emulator written in C#
Stars: ✭ 10,983 (+68543.75%)
Mutual labels:  emulation
Giovanni
A Gameboy Emulator for the Apple Watch
Stars: ✭ 823 (+5043.75%)
Mutual labels:  emulation
Udocker
A basic user tool to execute simple docker containers in batch or interactive systems without root privileges
Stars: ✭ 802 (+4912.5%)
Mutual labels:  emulation
Vipermonkey
A VBA parser and emulation engine to analyze malicious macros.
Stars: ✭ 697 (+4256.25%)
Mutual labels:  emulation

crates.io docs.rs

ludus

Ludus is a crate providing the core logic of an NES emulator. Unlike other crates, Ludus is not a standalone application. Instead, Ludus is a crate designed to be easily embedded in an application. For an example of using Ludus to make a GUI emulator, see ludus-emu.

The advantage of being headless is that Ludus is easily useable in contexts outside of a standalone application. For example, this crate could be used to train agents the play NES games, or to generate screenshots from NES games, or to generate plots of RAM, etc. By being headless, Ludus can be used in your own emulator application in whatever way you want.

Features

  • CPU emulation
  • Video emulation
  • Audio emulation
  • Parsing rom data from .ines files.
  • Mappers 0, 1, and 2, so many common games.

Usage

Let's first import the main types used in Ludus:

use ludus::*;

The main emulator type is Console. Before we can create a Console, we need a cartridge to play. We can create a Cart type by reading an .ines file.

let bytes: &[u8] = read_ines_bytes();
let cart = Cart::from_bytes(bytes).unwrap();

Creating a cartridge will naturally fail if the ROM data wasn't valid.

Once we have a cartridge, we can create a console to play this cartridge:

let console = Console::new(cart, sample_rate);

Creating a console requires a cartridge, as well as a sample rate for the audio process unit (APU). Normally, if you're using some crate that allows you to play audio to a device, you should have access to this sample_rate.

At any point in time we can reset the console like so:

console.reset();

We can also update the state of the buttons using the ButtonState struct:

let mut buttons = ButtonState::default();
buttons.a = true;
console.update_controller(buttons);

Now to actually start doing some emulation, we need to step the console forward. Anytime we advance emulation however, the APU might generate audio samples, and the PPU might generate video frames. To handle these, we need to provide a device that can handle the audio samples, and a device to handle the video frames.

For handling audio, we have the AudioDevice trait:

trait AudioDevice {
    fn push_sample(&mut self, sample: f32)
}

A device implementing this trait should be able to receive an audio sample, in the range [-1, 1] and to audio stuff with that information. The sample rate passed to the console determines how often the APU will generate samples and push them to this device.

For handling video, we have the VideoDevice trait:

trait VideoDevice {
    fn blit_pixels(&mut self, pixels: &PixelBuffer)
}

This device should be able to receive a frame of pixels, and display that on
screen, or whatever else you might want to do with the video data. The pixelbuffer contains 256x240 ARGB pixels, in row major format.

If you don't want to handle audio or video, you can simple create an empty struct that does nothing for both traits:

#[derive(Clone, Copy)]
pub struct NullDevice;

impl AudioDevice for NullDevice {
    fn push_sample(&mut self, sample: f32) {
    }
}

impl VideoDevice for NullDevice {
    fn blit_pixels(&mut self, pixels: &PixelBuffer) {
    }
}

Now that we have the devices set up, we can start doing some emulation.

The simplest method to advance the console is step:

pub fn step<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V) -> i32 where
    A: AudioDevice,
    V: VideoDevice,

This will advance the Console forward by one cpu cycle. This is only useful if you want to be able to see things advance very very slowly. If you're something automated, like a bot, you want to use step_frame instead, since most games won't even look at input more than once per frame anyways.

The next method is step_micros:

pub fn step_micros<'a, A, V>(
    &'a mut self,
    audio: &mut A,
    video: &mut V,
    micros: u32
) where
    A: AudioDevice,
    V: VideoDevice, 

This method will instead advance the emulator by a certain number of microseconds. This is the most useful method if you're implementing your own GUI and want to advance the emulator in some kind of game loop.

An example of doing such a loop might look like this:

let mut old = Instant::now();
loop {
    let now = Instant::now();
    let duration = now.duration_since(old);
    old = now;
    console.step_micros(audio, video, duration.subsec_micros());
}

The final method allows you to advance the emulator by a full frame:

pub fn step_frame<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V) where
    A: AudioDevice,
    V: VideoDevice,

This is useful if you're training a bot, because games will only look at input once per frame. So you'd set input for that frame, then advance once frame, then set input, etc. Note that this is not based on timing like the other methods, but by waiting for the ppu to reach the end of the current frame.

Resources

I relied heavily on this very nicely written open source emulator: https://github.com/fogleman/nes.

This page https://wiki.nesdev.com/w/index.php/NES_reference_guide was and still is my bible as I work on this project; kudos to the many people who've contributed in some way over the years.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].