rgy 0.1.0

No-std Rust GameBoy emulator library
Documentation
use cpal;
use log::*;
use minifb::{Scale, Window, WindowOptions};
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc, Mutex,
};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use rgy::{Key, Stream, VRAM_HEIGHT, VRAM_WIDTH};

#[derive(Clone)]
pub struct Hardware {
    rampath: Option<String>,
    vram: Arc<Mutex<Vec<u32>>>,
    pcm: SpeakerHandle,
    keystate: Arc<Mutex<HashMap<Key, bool>>>,
    escape: Arc<AtomicBool>,
}

struct Gui {
    window: Window,
    vram: Arc<Mutex<Vec<u32>>>,
    keystate: Arc<Mutex<HashMap<Key, bool>>>,
    escape: Arc<AtomicBool>,
}

impl Gui {
    fn new(
        vram: Arc<Mutex<Vec<u32>>>,
        keystate: Arc<Mutex<HashMap<Key, bool>>>,
        escape: Arc<AtomicBool>,
    ) -> Self {
        let title = if cfg!(feature = "color") {
            "Gay Boy Color"
        } else {
            "Gay Boy"
        };
        let window = match Window::new(
            title,
            VRAM_WIDTH,
            VRAM_HEIGHT,
            WindowOptions {
                resize: false,
                scale: Scale::X4,
                ..WindowOptions::default()
            },
        ) {
            Ok(win) => win,
            Err(err) => {
                panic!("Unable to create window {}", err);
            }
        };

        Self {
            window,
            vram,
            keystate,
            escape,
        }
    }

    fn run(mut self) {
        while !self.escape.load(Ordering::Relaxed) {
            std::thread::sleep(Duration::from_millis(10));
            self.vramupdate();
            self.keyupdate();
        }
    }

    fn vramupdate(&mut self) {
        let vram = self.vram.lock().unwrap().clone();
        self.window.update_with_buffer(&vram).unwrap();
    }

    fn keyupdate(&mut self) {
        if !self.window.is_open() {
            self.escape.store(true, Ordering::Relaxed);
        }

        for (_, v) in self.keystate.lock().unwrap().iter_mut() {
            *v = false;
        }

        if let Some(keys) = self.window.get_keys() {
            for k in keys {
                let gbk = match k {
                    minifb::Key::Right => Key::Right,
                    minifb::Key::Left => Key::Left,
                    minifb::Key::Up => Key::Up,
                    minifb::Key::Down => Key::Down,
                    minifb::Key::Z => Key::A,
                    minifb::Key::X => Key::B,
                    minifb::Key::Space => Key::Select,
                    minifb::Key::Enter => Key::Start,
                    minifb::Key::Escape => {
                        self.escape.store(true, Ordering::Relaxed);
                        return;
                    }
                    _ => continue,
                };

                match self.keystate.lock().unwrap().get_mut(&gbk) {
                    Some(v) => *v = true,
                    None => unreachable!(),
                }
            }
        }
    }
}

impl Hardware {
    pub fn new(rampath: Option<String>) -> Self {
        let vram = Arc::new(Mutex::new(vec![0; VRAM_WIDTH * VRAM_HEIGHT]));

        let pcm = Pcm::new();
        let handle = pcm.handle();
        pcm.run_forever();

        let mut keystate = HashMap::new();
        keystate.insert(Key::Right, false);
        keystate.insert(Key::Left, false);
        keystate.insert(Key::Up, false);
        keystate.insert(Key::Down, false);
        keystate.insert(Key::A, false);
        keystate.insert(Key::B, false);
        keystate.insert(Key::Select, false);
        keystate.insert(Key::Start, false);
        let keystate = Arc::new(Mutex::new(keystate));

        let escape = Arc::new(AtomicBool::new(false));

        Self {
            rampath,
            vram,
            pcm: handle,
            keystate,
            escape,
        }
    }

    pub fn run(self) {
        let bg = Gui::new(
            self.vram.clone(),
            self.keystate.clone(),
            self.escape.clone(),
        );
        bg.run();
    }
}

impl rgy::Hardware for Hardware {
    fn vram_update(&mut self, line: usize, buf: &[u32]) {
        let mut vram = self.vram.lock().unwrap();
        for i in 0..buf.len() {
            let base = line * VRAM_WIDTH;
            vram[base + i] = buf[i];
        }
    }

    fn joypad_pressed(&mut self, key: Key) -> bool {
        *self
            .keystate
            .lock()
            .unwrap()
            .get(&key)
            .expect("Logic error in keystate map")
    }

    fn sound_play(&mut self, stream: Box<dyn Stream>) {
        self.pcm.play(stream)
    }

    fn send_byte(&mut self, b: u8) {
        info!("Send byte: {:02x}", b);
    }

    fn recv_byte(&mut self) -> Option<u8> {
        None
    }

    fn clock(&mut self) -> u64 {
        let epoch = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Couldn't get epoch");
        epoch.as_micros() as u64
    }

    fn load_ram(&mut self, size: usize) -> Vec<u8> {
        let mut ram = vec![0; size];

        match &self.rampath {
            Some(path) => match File::open(path) {
                Ok(mut fs) => {
                    fs.read_exact(&mut ram).expect("Couldn't read file");
                    ram
                }
                Err(e) => {
                    warn!("Couldn't open RAM file `{}`: {}", path, e);
                    ram
                }
            },
            None => ram,
        }
    }

    fn save_ram(&mut self, ram: &[u8]) {
        match &self.rampath {
            Some(path) => {
                let mut fs = File::create(path).expect("Couldn't open file");
                fs.write_all(ram).expect("Couldn't write file");
            }
            None => {}
        }
    }

    fn sched(&mut self) -> bool {
        !self.escape.load(Ordering::Relaxed)
    }
}

pub struct Pcm {
    tx: Sender<SpeakerCmd>,
    rx: Receiver<SpeakerCmd>,
}

impl Pcm {
    pub fn new() -> Pcm {
        let (tx, rx) = mpsc::channel();

        Pcm { tx, rx }
    }

    pub fn handle(&self) -> SpeakerHandle {
        SpeakerHandle {
            tx: self.tx.clone(),
        }
    }

    pub fn run_forever(self) {
        std::thread::spawn(move || {
            self.run();
        });
    }

    pub fn run(self) {
        let device = cpal::default_output_device().expect("Failed to get default output device");
        let format = device
            .default_output_format()
            .expect("Failed to get default output format");
        let sample_rate = format.sample_rate.0;
        let event_loop = cpal::EventLoop::new();
        let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
        event_loop.play_stream(stream_id.clone());

        let mut stream = None;

        event_loop.run(move |_, data| {
            match self.rx.try_recv() {
                Ok(SpeakerCmd::Play(s)) => {
                    stream = Some(s);
                }
                Ok(SpeakerCmd::Stop) => {
                    stream = None;
                }
                Err(_) => {}
            }

            match data {
                cpal::StreamData::Output {
                    buffer: cpal::UnknownTypeOutputBuffer::U16(_buffer),
                } => unimplemented!(),
                cpal::StreamData::Output {
                    buffer: cpal::UnknownTypeOutputBuffer::I16(_buffer),
                } => unimplemented!(),
                cpal::StreamData::Output {
                    buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer),
                } => {
                    for sample in buffer.chunks_mut(format.channels as usize) {
                        let value = match &mut stream {
                            Some(s) => {
                                ((s.next(sample_rate) as u64 * 100 / s.max() as u64) as f32 / 100.0)
                            }
                            None => 0.0,
                        };

                        for out in sample.iter_mut() {
                            *out = value;
                        }
                    }
                }
                _ => (),
            }
        });
    }
}

#[allow(unused)]
enum SpeakerCmd {
    Play(Box<dyn Stream>),
    Stop,
}

#[derive(Clone)]
pub struct SpeakerHandle {
    tx: Sender<SpeakerCmd>,
}

impl SpeakerHandle {
    fn play(&self, stream: Box<dyn Stream>) {
        let _ = self.tx.send(SpeakerCmd::Play(stream));
    }

    #[allow(unused)]
    fn stop(&self) {
        let _ = self.tx.send(SpeakerCmd::Stop);
    }
}