Skip to content

Commit

Permalink
Support desktop platforms with SDL2
Browse files Browse the repository at this point in the history
  • Loading branch information
nathsou committed Jul 28, 2023
1 parent 37abdb1 commit 25a426e
Show file tree
Hide file tree
Showing 22 changed files with 388 additions and 267 deletions.
1 change: 0 additions & 1 deletion 3ds/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const LEFT_X_OFFSET_TOP: usize = (TOP_SCREEN_WIDTH - NES_SCREEN_WIDTH) / 2;
const LEFT_X_OFFSET_BOTTOM: usize = (BOTTOM_SCREEN_WIDTH - NES_SCREEN_WIDTH) / 2;
static LOGO: &[u8] = include_bytes!("../assets/logo.rgb");

#[inline]
fn is_key_active(hid: &Hid, key: KeyPad) -> bool {
hid.keys_down().contains(key) || hid.keys_held().contains(key)
}
Expand Down
1 change: 1 addition & 0 deletions desktop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
149 changes: 149 additions & 0 deletions desktop/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions desktop/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "desktop"
version = "0.1.0"
edition = "2021"

[dependencies]
nessy = { path = "../" }
sdl2 = "0.35.2"
147 changes: 147 additions & 0 deletions desktop/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
extern crate nessy;

use std::collections::HashMap;

use nessy::{
controller::{Joypad, JoypadStatus},
cpu::rom::ROM,
Nes, SCREEN_HEIGHT, SCREEN_WIDTH,
};
use sdl2::{
audio::{AudioCallback, AudioSpecDesired},
event::Event,
keyboard::Keycode,
pixels::PixelFormatEnum,
EventPump,
};

const SCALE_FACTOR: usize = 2;
const SAMPLE_RATE: f64 = 44_100.0;

fn build_controller_map() -> HashMap<Keycode, JoypadStatus> {
let mut controller_map = HashMap::new();
controller_map.insert(Keycode::W, JoypadStatus::UP);
controller_map.insert(Keycode::S, JoypadStatus::DOWN);
controller_map.insert(Keycode::A, JoypadStatus::LEFT);
controller_map.insert(Keycode::D, JoypadStatus::RIGHT);
controller_map.insert(Keycode::L, JoypadStatus::A);
controller_map.insert(Keycode::K, JoypadStatus::B);
controller_map.insert(Keycode::Return, JoypadStatus::START);
controller_map.insert(Keycode::Space, JoypadStatus::SELECT);
controller_map
}

struct APUCallback<'a> {
nes: &'a mut Nes,
avoid_underruns: bool,
}

impl<'a> AudioCallback for APUCallback<'a> {
type Channel = f32;

fn callback(&mut self, out: &mut [f32]) {
self.nes.fill_audio_buffer(out, self.avoid_underruns);
out.iter_mut().for_each(|s| *s *= 4.0);
}
}

fn handle_events(
event_pump: &mut EventPump,
controller: &mut Joypad,
controller_map: &HashMap<Keycode, JoypadStatus>,
) {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
std::process::exit(0);
}
Event::KeyDown { keycode, .. } => {
if let Some(&button) = keycode.and_then(|k| controller_map.get(&k)) {
controller.status.insert(button);
}
}
Event::KeyUp { keycode, .. } => {
if let Some(&button) = keycode.and_then(|k| controller_map.get(&k)) {
controller.status.remove(button);
}
}
_ => {}
}
}
}

fn main() {
let args = std::env::args().take(2).collect::<Vec<_>>();

if args.len() < 2 {
eprintln!("usage: nessy rom.nes");
} else {
let rom_path = &args[1];
let bytes = std::fs::read(rom_path).unwrap();
let rom = ROM::new(bytes).unwrap();
let mut nes = Nes::new(rom, SAMPLE_RATE);

let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let audio_subsystem = sdl_context.audio().unwrap();
let desired_audio_spec = AudioSpecDesired {
freq: Some(SAMPLE_RATE as i32),
channels: Some(1), // mono,
samples: Some(1024),
};

let window = video_subsystem
.window(
"nessy",
(SCREEN_WIDTH * SCALE_FACTOR) as u32,
(SCREEN_HEIGHT * SCALE_FACTOR) as u32,
)
.position_centered()
.build()
.expect("could not initialize video subsystem");

let mut canvas = window
.into_canvas()
.present_vsync()
.build()
.expect("could not make a canvas");

canvas
.set_scale(SCALE_FACTOR as f32, SCALE_FACTOR as f32)
.unwrap();

let creator = canvas.texture_creator();
let mut texture = creator
.create_texture_target(
PixelFormatEnum::RGB24,
SCREEN_WIDTH as u32,
SCREEN_HEIGHT as u32,
)
.unwrap();

let mut event_pump = sdl_context.event_pump().unwrap();
let controller_map = build_controller_map();

let audio_device = audio_subsystem
.open_playback(None, &desired_audio_spec, |_| APUCallback {
nes: &mut nes,
avoid_underruns: false,
})
.unwrap();

audio_device.resume();

loop {
handle_events(&mut event_pump, nes.get_joypad1_mut(), &controller_map);
nes.next_frame();
let frame = nes.get_frame();
texture.update(None, frame, SCREEN_WIDTH * 3).unwrap();
canvas.copy(&texture, None, None).unwrap();
canvas.present();
}
}
}
7 changes: 0 additions & 7 deletions src/apu/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ pub struct Timer {
}

impl Timer {
#[inline]
pub fn step(&mut self) -> bool {
if self.counter == 0 {
self.counter = self.period;
Expand All @@ -32,29 +31,24 @@ pub struct LengthCounter {
}

impl LengthCounter {
#[inline]
pub fn reset_to_zero(&mut self) {
self.counter = 0;
}

#[inline]
pub fn step(&mut self) {
if self.enabled && self.counter > 0 {
self.counter -= 1;
}
}

#[inline]
pub fn set(&mut self, val: u8) {
self.counter = LENGTH_LOOKUP[val as usize];
}

#[inline]
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}

#[inline]
pub fn is_zero(&self) -> bool {
self.counter == 0
}
Expand Down Expand Up @@ -90,7 +84,6 @@ impl Envelope {
}
}

#[inline]
pub fn output(&self) -> u8 {
if self.constant_mode {
self.constant_volume
Expand Down
Loading

0 comments on commit 25a426e

Please sign in to comment.