diff options
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/bin.rs | 164 | ||||
-rw-r--r-- | src/controller.rs | 42 |
3 files changed, 117 insertions, 91 deletions
@@ -1,6 +1,6 @@ [package] name = "runes" -version = "0.1.7" +version = "0.1.8" authors = ["Determinant <[email protected]>"] description = "No-std NES emulator library and minimal emulator written purely in Rust." repository = "https://github.com/Determinant/runes" @@ -5,7 +5,7 @@ use std::sync::{Mutex, Condvar}; use std::io::{Read, Write}; use std::mem::transmute; use std::process::exit; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; extern crate sdl2; #[macro_use] extern crate clap; @@ -30,7 +30,7 @@ use ppu::PPU; use apu::APU; use memory::{CPUMemory, PPUMemory}; use cartridge::{BankType, MirrorType, Cartridge}; -use controller::stdctl; +use controller::{InputPoller, stdctl}; const RGB_COLORS: [u32; 64] = [ 0x666666, 0x002a88, 0x1412a7, 0x3b00a4, 0x5c007e, 0x6e0040, 0x6c0600, 0x561d00, @@ -155,17 +155,6 @@ impl utils::Write for FileIO { } } -struct SDLWindow<'a> { - canvas: sdl2::render::WindowCanvas, - events: sdl2::EventPump, - frame_buffer: [u8; FB_SIZE], - texture: sdl2::render::Texture, - p1_button_state: u8, - p1_ctl: &'a stdctl::Joystick, - p1_keymap: [u8; 256], - copy_area: Option<Rect>, - exit_flag: &'a Cell<bool> -} macro_rules! gen_keymap { ($tab: ident, [$($x: expr, $y: expr), *]) => { @@ -173,47 +162,24 @@ macro_rules! gen_keymap { }; } -impl<'a> SDLWindow<'a> { - fn new(sdl_context: &'a sdl2::Sdl, - p1_ctl: &'a stdctl::Joystick, - pixel_scale: u32, - full_screen: bool, - exit_flag: &'a Cell<bool>) -> Self { - use Keycode::*; - let video_subsystem = sdl_context.video().unwrap(); - let mut actual_height = PIX_HEIGHT * pixel_scale; - let actual_width = PIX_WIDTH * pixel_scale; - let mut copy_area = None; - if !full_screen { - actual_height -= 16 * pixel_scale; - copy_area = Some(Rect::new(0, 8, PIX_WIDTH, PIX_HEIGHT - 16)); - } - let window = video_subsystem.window("RuNES", actual_width, actual_height) - .position_centered() - .opengl() - .build() - .unwrap(); - let mut canvas = window.into_canvas() - .accelerated() - .present_vsync() - .build().unwrap(); - let texture_creator = canvas.texture_creator(); - canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); - canvas.clear(); - canvas.present(); - let mut res = SDLWindow { - canvas, - events: sdl_context.event_pump().unwrap(), - frame_buffer: [0; FB_SIZE], - texture: texture_creator.create_texture_streaming( - sdl2::pixels::PixelFormatEnum::RGB24, - PIX_WIDTH, PIX_HEIGHT).unwrap(), - p1_button_state: 0, - p1_ctl, p1_keymap: [stdctl::NULL; 256], - copy_area, exit_flag +struct SDLEventPoller { + events: RefCell<sdl2::EventPump>, + p1_button_state: Cell<u8>, + keymap: [u8; 256], + exit_flag: Cell<bool> +} + +impl SDLEventPoller { + fn new(_events: sdl2::EventPump) -> Self { + let mut res = SDLEventPoller { + events: RefCell::new(_events), + p1_button_state: Cell::new(0), + exit_flag: Cell::new(false), + keymap: [stdctl::NULL; 256] }; + use Keycode::*; { - let keymap = &mut res.p1_keymap; + let keymap = &mut res.keymap; gen_keymap!(keymap, [I, stdctl::UP, K, stdctl::DOWN, J, stdctl::LEFT, @@ -232,26 +198,78 @@ impl<'a> SDLWindow<'a> { } #[inline] - fn poll(&mut self) -> bool { + fn is_exiting(&self) -> bool { + self.exit_flag.get() + } +} + +impl InputPoller for SDLEventPoller { + #[inline] + fn poll(&self) -> u8 { use Keycode::*; - let p1_keymap = &self.p1_keymap; - for event in self.events.poll_iter() { + let keymap = &self.keymap; + let mut ns = self.p1_button_state.get(); + for event in self.events.borrow_mut().poll_iter() { match event { Event::Quit {..} | Event::KeyDown { keycode: Some(Escape), .. } => { - return true; + self.exit_flag.set(true) }, Event::KeyDown { keycode: Some(c), .. } => { - self.p1_button_state |= p1_keymap[(c as usize) & 0xff]; - self.p1_ctl.set(self.p1_button_state) + ns |= keymap[(c as usize) & 0xff] }, Event::KeyUp { keycode: Some(c), .. } => { - self.p1_button_state &= !p1_keymap[(c as usize) & 0xff]; - self.p1_ctl.set(self.p1_button_state) + ns &= !keymap[(c as usize) & 0xff] }, _ => () } } - false + self.p1_button_state.set(ns); + ns + } +} + +struct SDLWindow<'a> { + canvas: sdl2::render::WindowCanvas, + frame_buffer: [u8; FB_SIZE], + texture: sdl2::render::Texture, + copy_area: Option<Rect>, + event: &'a SDLEventPoller +} + +impl<'a> SDLWindow<'a> { + fn new(video_subsystem: &sdl2::VideoSubsystem, + event: &'a SDLEventPoller, + pixel_scale: u32, + full_screen: bool) -> Self { + let mut actual_height = PIX_HEIGHT * pixel_scale; + let actual_width = PIX_WIDTH * pixel_scale; + let mut copy_area = None; + if !full_screen { + actual_height -= 16 * pixel_scale; + copy_area = Some(Rect::new(0, 8, PIX_WIDTH, PIX_HEIGHT - 16)); + } + let window = video_subsystem.window("RuNES", actual_width, actual_height) + .position_centered() + .opengl() + .build() + .unwrap(); + let mut canvas = window.into_canvas() + .accelerated() + .present_vsync() + .build().unwrap(); + let texture_creator = canvas.texture_creator(); + canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); + canvas.clear(); + canvas.present(); + SDLWindow { + canvas, + frame_buffer: [0; FB_SIZE], + texture: texture_creator.create_texture_streaming( + sdl2::pixels::PixelFormatEnum::RGB24, + PIX_WIDTH, PIX_HEIGHT).unwrap(), + event, + copy_area + } } } @@ -280,11 +298,7 @@ impl<'a> ppu::Screen for SDLWindow<'a> { self.canvas.clear(); self.canvas.copy(&self.texture, self.copy_area, None).unwrap(); self.canvas.present(); - if self.poll() { - /* - */ - self.exit_flag.set(true) - } + self.event.poll(); } } @@ -511,8 +525,10 @@ fn main() { println!("read prg {}", file.read(&mut prg_rom[..]).unwrap()); println!("read chr {}", file.read(&mut chr_rom[..]).unwrap()); - /* audio */ + /* SDL setup */ let sdl_context = sdl2::init().unwrap(); + let event = SDLEventPoller::new(sdl_context.event_pump().unwrap()); + let video_subsystem = sdl_context.video().unwrap(); let audio_subsystem = sdl_context.audio().unwrap(); let audio_sync = AudioSync { time_barrier: Condvar::new(), buffer: Mutex::new((CircularBuffer::new(), @@ -526,12 +542,10 @@ fn main() { let device = audio_subsystem.open_playback(None, &desired_spec, |_| { SDLAudioPlayback(&audio_sync) }).unwrap(); - /* P1 controller */ - let p1ctl = stdctl::Joystick::new(); + let mut win = SDLWindow::new(&video_subsystem, &event, /*&p1ctl, */ scale, full); + /* construct mapper from cartridge data */ let cart = SimpleCart::new(chr_rom, prg_rom, sram, mirror); - let exit_flag = Cell::new(false); - let mut win = Box::new(SDLWindow::new(&sdl_context, &p1ctl, scale, full, &exit_flag)); let mut m: Box<mapper::Mapper> = match mapper_id { 0 | 2 => Box::new(mapper::Mapper2::new(cart)), 1 => Box::new(mapper::Mapper1::new(cart)), @@ -539,9 +553,12 @@ fn main() { _ => panic!("unsupported mapper {}", mapper_id) }; + /* P1 controller */ + let p1ctl = stdctl::Joystick::new(&event); + let mapper = mapper::RefMapper::new(&mut (*m) as &mut mapper::Mapper); let mut cpu = CPU::new(CPUMemory::new(&mapper, Some(&p1ctl), None)); - let mut ppu = PPU::new(PPUMemory::new(&mapper), &mut (*win)); + let mut ppu = PPU::new(PPUMemory::new(&mapper), &mut win); let mut apu = APU::new(&mut spkr); let cpu_ptr = &mut cpu as *mut CPU; cpu.mem.bus.attach(cpu_ptr, &mut ppu, &mut apu); @@ -583,7 +600,8 @@ fn main() { while cpu.cycle > 0 { cpu.mem.bus.tick() } - if exit_flag.get() { + + if event.is_exiting() { { let mut file = FileIO(File::create(match save_state_name { Some(s) => s.to_string(), diff --git a/src/controller.rs b/src/controller.rs index bb78769..52078e5 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -8,10 +8,14 @@ pub trait Controller { fn save(&self, writer: &mut Write) -> bool; } +pub trait InputPoller { + fn poll(&self) -> u8; +} + pub mod stdctl { use utils::{Read, Write, load_prefix, save_prefix}; use core::cell::Cell; - use controller::Controller; + use controller::{Controller, InputPoller}; pub const A: u8 = 1 << 0; pub const B: u8 = 1 << 1; pub const SELECT: u8 = 1 << 2; @@ -23,35 +27,39 @@ pub mod stdctl { pub const NULL: u8 = 0; #[repr(C)] - pub struct Joystick { + pub struct Joystick<'a> { strobe: Cell<bool>, reg: Cell<u8>, - back_reg: Cell<u8> + poller: &'a InputPoller, } - impl Joystick { - pub fn new() -> Self { - Joystick{reg: Cell::new(0), strobe: Cell::new(false), back_reg: Cell::new(0)} - } - - pub fn set(&self, buttons: u8) { - self.reg.set(buttons); - self.back_reg.set(buttons); + impl<'a> Joystick<'a> { + pub fn new(poller: &'a InputPoller) -> Self { + Joystick{ + reg: Cell::new(0), + strobe: Cell::new(false), + poller + } } } - impl Controller for Joystick { + impl<'a> Controller for Joystick<'a> { fn read(&self) -> u8 { - let res = self.reg.get() & 1; - if !self.strobe.get() { - self.reg.set(self.reg.get() >> 1); + if self.strobe.get() { + self.reg.set(self.poller.poll()); + self.reg.get() & 1 + } else { + let old = self.reg.get(); + self.reg.set(old >> 1); + old & 1 } - res } fn write(&self, data: u8) { self.strobe.set(data & 1 == 1); - self.reg.set(self.back_reg.get()); + if self.strobe.get() { + self.reg.set(self.poller.poll()) + } } fn load(&mut self, reader: &mut Read) -> bool { |