use sdl2::{
audio::{AudioQueue, AudioSpecDesired},
event::{Event, WindowEvent},
keyboard::Scancode,
rect::Point,
};
use std::{
env,
fs::File,
io::Read,
process::exit,
thread::sleep,
time::{Duration, Instant},
};
use yane::core::{Cartridge, Controller, Nes, Settings, CPU_CLOCK_SPEED};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Please provide an iNes file to run");
exit(0);
}
let ines_file = &args[1];
println!("Running {}", ines_file);
let ines_bytes = {
let mut buf = Vec::new();
File::open(ines_file)
.expect("Unable to open iNes file")
.read_to_end(&mut buf)
.expect("Unable to read iNes file");
buf
};
let mut nes = Nes::with_cartridge(Cartridge::from_ines(&ines_bytes, None).unwrap());
let settings = Settings::default();
let sdl = sdl2::init().unwrap();
let video = sdl.video().unwrap();
let window = video.window("Example 2 - SDL", 256, 240).build().unwrap();
let mut canvas = window.into_canvas().build().unwrap();
let audio = sdl.audio().unwrap();
let queue: AudioQueue<f32> = audio
.open_queue::<f32, _>(
None,
&AudioSpecDesired {
freq: Some(44_800),
channels: Some(1),
samples: None,
},
)
.unwrap();
let mut sample_queue = Vec::new();
let mut event_pump = sdl.event_pump().unwrap();
let start_time = Instant::now();
let mut emu_duration = Duration::ZERO;
queue.clear();
while !event_pump.poll_iter().any(|e| match e {
Event::Window { win_event, .. } => win_event == WindowEvent::Close,
_ => false,
}) {
let cpu_cycles = nes.advance_frame(&settings).unwrap();
let rgb_output: [[[u8; 3]; 256]; 240] = nes.ppu.rgb_output();
for y in 0..240 {
for x in 0..256 {
let pixel = rgb_output[y][x];
canvas.set_draw_color((pixel[0], pixel[1], pixel[2]));
canvas.draw_point(Point::new(x as i32, y as i32)).unwrap();
}
}
canvas.present();
sample_queue.extend_from_slice(&nes.apu.sample_queue());
let chunks = sample_queue
.chunks_exact((CPU_CLOCK_SPEED as f64 / queue.spec().freq as f64).ceil() as usize);
let remainder = chunks.remainder();
let resampled_output: Vec<f32> = chunks
.map(|c| c.iter().sum::<f32>() / c.len() as f32)
.collect();
if queue.size() > queue.spec().freq as u32 {
println!("Clearing queue");
queue.clear();
}
queue.queue_audio(&resampled_output).unwrap();
sample_queue = remainder.to_vec();
queue.resume();
let keys: Vec<Scancode> = event_pump.keyboard_state().pressed_scancodes().collect();
let controller_state = Controller {
up: keys.contains(&Scancode::W),
left: keys.contains(&Scancode::A),
down: keys.contains(&Scancode::S),
right: keys.contains(&Scancode::D),
a: keys.contains(&Scancode::N),
b: keys.contains(&Scancode::M),
start: keys.contains(&Scancode::R),
select: keys.contains(&Scancode::T),
};
nes.set_controller_state(0, controller_state);
emu_duration +=
Duration::from_nanos(1_000_000_000 * cpu_cycles as u64 / CPU_CLOCK_SPEED as u64);
let real_duration = Instant::now().duration_since(start_time);
if emu_duration > real_duration {
sleep(emu_duration - real_duration);
}
}
}