aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <[email protected]>2018-01-21 12:53:26 -0500
committerDeterminant <[email protected]>2018-01-21 12:53:26 -0500
commit38abb8c04fc9cbf2435dd8933678be1c0d2d763d (patch)
treea1eee139c6ed1622b841dc9fe5fa3d08744e3f12
parent25dc0702403896cae8c8b0e123bc9a0161fded2c (diff)
change the way of driving controllers
-rw-r--r--Cargo.toml2
-rw-r--r--src/bin.rs164
-rw-r--r--src/controller.rs42
3 files changed, 117 insertions, 91 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 62a9fe8..6b90d64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/src/bin.rs b/src/bin.rs
index ba3c5a2..08f2f59 100644
--- a/src/bin.rs
+++ b/src/bin.rs
@@ -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 {