Get rid of the VideoInterface trait

Former-commit-id: 30a14ff0609b385b80b4c17a8f70e89fce816509
Former-commit-id: 519aeea19b3faa37b732463ecfdddb9730322021
This commit is contained in:
Michel Heily 2022-09-12 01:13:01 +03:00
parent 0f64f07133
commit 4db32b1af2
15 changed files with 98 additions and 209 deletions

View file

@ -69,13 +69,16 @@ impl DebugRead for SimpleMemory {
impl MemoryGdbInterface for SimpleMemory {
fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize {
let memory_map = format!(r#"<?xml version="1.0"?>
let memory_map = format!(
r#"<?xml version="1.0"?>
<!DOCTYPE memory-map
PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
"http://sourceware.org/gdb/gdb-memory-map.dtd">
<memory-map>
<memory type="ram" start="0x0" length="{}"/>
</memory-map>"#, self.data.len());
</memory-map>"#,
self.data.len()
);
copy_range_to_buf(memory_map.trim().as_bytes(), offset, length, buf)
}
}

View file

@ -2,5 +2,5 @@
rustboyadvance-core crate provides a simple yet effective performance benchmark.
to run it use `cargo bench --manifest-path ./core/Cargo.toml --features no_video_interface` from the repository root
to run it use `cargo bench --manifest-path ./core/Cargo.toml` from the repository root

View file

@ -55,5 +55,3 @@ default = []
elf_support = []
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
gdb = ["gdbstub"]
# For use for ports where VideoInterface is not needed like wasm & jni
no_video_interface = []

View file

@ -11,7 +11,6 @@ use rustboyadvance_utils::elf::{load_elf, GoblinError};
use rustboyadvance_utils::read_bin_file;
use zip::ZipArchive;
pub enum LoadRom {
#[cfg(feature = "elf_support")]
Elf {

View file

@ -16,8 +16,6 @@ use super::sysbus::SysBus;
use super::timer::Timers;
use super::AudioInterface;
#[cfg(not(feature = "no_video_interface"))]
use super::VideoInterface;
use arm7tdmi::{self, Arm7tdmiCore};
use rustboyadvance_utils::Shared;
@ -28,8 +26,6 @@ pub struct GameBoyAdvance {
pub io_devs: Shared<IoDevices>,
pub scheduler: SharedScheduler,
interrupt_flags: SharedInterruptFlags,
#[cfg(not(feature = "no_video_interface"))]
pub video_device: Rc<RefCell<dyn VideoInterface>>,
pub audio_device: Rc<RefCell<dyn AudioInterface>>,
}
@ -67,7 +63,6 @@ impl GameBoyAdvance {
pub fn new(
bios_rom: Box<[u8]>,
gamepak: Cartridge,
#[cfg(not(feature = "no_video_interface"))] video_device: Rc<RefCell<dyn VideoInterface>>,
audio_device: Rc<RefCell<dyn AudioInterface>>,
) -> GameBoyAdvance {
// Warn the user if the bios is not the real one
@ -108,8 +103,6 @@ impl GameBoyAdvance {
cpu,
sysbus,
io_devs,
#[cfg(not(feature = "no_video_interface"))]
video_device,
audio_device,
scheduler,
interrupt_flags,
@ -124,7 +117,6 @@ impl GameBoyAdvance {
savestate: &[u8],
bios: Box<[u8]>,
rom: Box<[u8]>,
#[cfg(not(feature = "no_video_interface"))] video_device: Rc<RefCell<dyn VideoInterface>>,
audio_device: Rc<RefCell<dyn AudioInterface>>,
) -> bincode::Result<GameBoyAdvance> {
let decoded: Box<SaveState> = bincode::deserialize_from(savestate)?;
@ -156,8 +148,6 @@ impl GameBoyAdvance {
sysbus,
io_devs,
interrupt_flags: interrupts,
#[cfg(not(feature = "no_video_interface"))]
video_device,
audio_device,
scheduler,
})
@ -308,12 +298,7 @@ impl GameBoyAdvance {
let apu = &mut io.sound;
Some(timers.handle_overflow_event(channel_id, event_time, apu, dmac))
}
EventType::Gpu(gpu_event) => Some(io.gpu.on_event(
gpu_event,
&mut *self.sysbus,
#[cfg(not(feature = "no_video_interface"))]
&self.video_device,
)),
EventType::Gpu(gpu_event) => Some(io.gpu.on_event(gpu_event, &mut *self.sysbus)),
EventType::Apu(event) => Some(io.sound.on_event(event, &self.audio_device)),
};
if let Some((new_event, when)) = new_event {
@ -380,8 +365,6 @@ impl GameBoyAdvance {
breakpoint
}
/// Query the emulator for the recently drawn framebuffer.
/// for use with implementations where the VideoInterface is not a viable option.
pub fn get_frame_buffer(&self) -> &[u32] {
self.sysbus.io.gpu.get_frame_buffer()
}
@ -401,17 +384,15 @@ mod tests {
use crate::cartridge::GamepakBuilder;
use arm7tdmi::memory::BusIO;
struct DummyInterface {}
struct DummyAudio {}
impl DummyInterface {
fn new() -> DummyInterface {
DummyInterface {}
impl DummyAudio {
fn new() -> DummyAudio {
DummyAudio {}
}
}
#[cfg(not(feature = "no_video_interface"))]
impl VideoInterface for DummyInterface {}
impl AudioInterface for DummyInterface {}
impl AudioInterface for DummyAudio {}
fn make_mock_gba(rom: &[u8]) -> GameBoyAdvance {
let bios = vec![0; 0x4000].into_boxed_slice();
@ -421,14 +402,8 @@ mod tests {
.without_backup_to_file()
.build()
.unwrap();
let dummy = Rc::new(RefCell::new(DummyInterface::new()));
let mut gba = GameBoyAdvance::new(
bios,
cartridge,
#[cfg(not(feature = "no_video_interface"))]
dummy.clone(),
dummy.clone(),
);
let dummy = Rc::new(RefCell::new(DummyAudio::new()));
let mut gba = GameBoyAdvance::new(bios, cartridge, dummy.clone());
gba.skip_bios();
gba

View file

@ -1,8 +1,3 @@
#[cfg(not(feature = "no_video_interface"))]
use std::cell::RefCell;
#[cfg(not(feature = "no_video_interface"))]
use std::rc::Rc;
use num::FromPrimitive;
use serde::{Deserialize, Serialize};
@ -13,8 +8,6 @@ use super::dma::{DmaNotifer, TIMING_HBLANK, TIMING_VBLANK};
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
use super::sched::{EventType, FutureEvent, GpuEvent, Scheduler};
pub use super::sysbus::consts::*;
#[cfg(not(feature = "no_video_interface"))]
use super::VideoInterface;
mod render;
@ -107,9 +100,6 @@ impl Default for ObjBufferEntry {
}
}
#[cfg(not(feature = "no_video_interface"))]
type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
#[derive(Serialize, Deserialize, Clone, DebugStub)]
pub struct Gpu {
interrupt_flags: SharedInterruptFlags,
@ -369,11 +359,7 @@ impl Gpu {
(GpuEvent::HBlank, CYCLES_HBLANK)
}
fn handle_hblank_end<D: DmaNotifer>(
&mut self,
dma_notifier: &mut D,
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
) -> FutureGpuEvent {
fn handle_hblank_end<D: DmaNotifer>(&mut self, dma_notifier: &mut D) -> FutureGpuEvent {
self.update_vcount(self.vcount + 1);
if self.vcount < DISPLAY_HEIGHT {
@ -401,9 +387,6 @@ impl Gpu {
dma_notifier.notify(TIMING_VBLANK);
#[cfg(not(feature = "no_video_interface"))]
video_device.borrow_mut().render(&self.frame_buffer);
self.obj_buffer_reset();
(GpuEvent::VBlankHDraw, CYCLES_HDRAW)
@ -432,22 +415,13 @@ impl Gpu {
}
}
pub fn on_event<D>(
&mut self,
event: GpuEvent,
dma_notifier: &mut D,
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
) -> FutureEvent
pub fn on_event<D>(&mut self, event: GpuEvent, dma_notifier: &mut D) -> FutureEvent
where
D: DmaNotifer,
{
let (event, when) = match event {
GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier),
GpuEvent::HBlank => self.handle_hblank_end(
dma_notifier,
#[cfg(not(feature = "no_video_interface"))]
video_device,
),
GpuEvent::HBlank => self.handle_hblank_end(dma_notifier),
GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(),
GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(),
};
@ -549,26 +523,10 @@ mod tests {
fn notify(&mut self, _timing: u16) {}
}
#[derive(Default)]
struct TestVideoInterface {
frame_counter: usize,
}
#[cfg(not(feature = "no_video_interface"))]
impl VideoInterface for TestVideoInterface {
fn render(&mut self, _buffer: &[u32]) {
self.frame_counter += 1;
}
}
#[test]
fn test_gpu_state_machine() {
let mut sched = Scheduler::new();
let mut gpu = Gpu::new(&mut sched, Rc::new(Cell::new(Default::default())));
#[cfg(not(feature = "no_video_interface"))]
let video = Rc::new(RefCell::new(TestVideoInterface::default()));
#[cfg(not(feature = "no_video_interface"))]
let video_clone: VideoDeviceRcRefCell = video.clone();
let mut dma_notifier = NopDmaNotifer;
gpu.dispstat.vcount_setting = 0;
@ -580,12 +538,7 @@ mod tests {
let (event, event_time) = sched.pop_pending_event().unwrap();
assert_eq!(event_time, sched.timestamp());
let next_event = match event {
EventType::Gpu(event) => gpu.on_event(
event,
&mut dma_notifier,
#[cfg(not(feature = "no_video_interface"))]
&video_clone,
),
EventType::Gpu(event) => gpu.on_event(event, &mut dma_notifier),
_ => panic!("Found unexpected event in queue!"),
};
sched.schedule(next_event);
@ -594,8 +547,6 @@ mod tests {
for line in 0..160 {
println!("line = {}", line);
#[cfg(not(feature = "no_video_interface"))]
assert_eq!(video.borrow().frame_counter, 0);
assert_eq!(gpu.vcount, line);
assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HDraw)));
assert_eq!(gpu.dispstat.hblank_flag, false);
@ -613,9 +564,6 @@ mod tests {
assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), false);
}
#[cfg(not(feature = "no_video_interface"))]
assert_eq!(video.borrow().frame_counter, 1);
for line in 0..68 {
println!("line = {}", 160 + line);
assert_eq!(gpu.dispstat.hblank_flag, false);
@ -638,8 +586,6 @@ mod tests {
update!(CYCLES_HBLANK);
}
#[cfg(not(feature = "no_video_interface"))]
assert_eq!(video.borrow().frame_counter, 1);
assert_eq!(sched.timestamp(), CYCLES_FULL_REFRESH);
assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), true);

View file

@ -55,12 +55,6 @@ pub(crate) mod overrides;
#[cfg(feature = "debugger")]
pub mod debugger;
#[cfg(not(feature = "no_video_interface"))]
pub trait VideoInterface {
#[allow(unused_variables)]
fn render(&mut self, buffer: &[u32]) {}
}
pub type StereoSample<T> = [T; 2];
pub trait AudioInterface {
@ -129,8 +123,6 @@ pub mod prelude {
pub use super::debugger::Debugger;
pub use super::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
pub use super::Bus;
#[cfg(not(feature = "no_video_interface"))]
pub use super::VideoInterface;
pub use super::{AudioInterface, StereoSample};
pub use super::{GBAError, GBAResult, GameBoyAdvance};
pub use rustboyadvance_utils::{read_bin_file, write_bin_file};

View file

@ -6,16 +6,15 @@ use std::rc::Rc;
use rustboyadvance_core::prelude::*;
use rustboyadvance_core::util::FpsCounter;
struct BenchmarkHardware {}
struct DummyAudio {}
impl BenchmarkHardware {
fn new() -> BenchmarkHardware {
BenchmarkHardware {}
impl DummyAudio {
fn new() -> DummyAudio {
DummyAudio {}
}
}
impl VideoInterface for BenchmarkHardware {}
impl AudioInterface for BenchmarkHardware {}
impl AudioInterface for DummyAudio {}
fn main() {
if env::args().count() < 3 {
@ -36,14 +35,9 @@ fn main() {
.build()
.unwrap();
let dummy = Rc::new(RefCell::new(BenchmarkHardware::new()));
let dummy = Rc::new(RefCell::new(DummyAudio::new()));
let mut gba = GameBoyAdvance::new(
bios.into_boxed_slice(),
gamepak,
dummy.clone(),
dummy.clone(),
);
let mut gba = GameBoyAdvance::new(bios.into_boxed_slice(), gamepak, dummy.clone());
gba.skip_bios();
let mut fps_counter = FpsCounter::default();

View file

@ -10,8 +10,8 @@ publish = false
crate-type = ["staticlib", "cdylib"]
[dependencies]
rustboyadvance-core = {path = "../../core/", features = ["no_video_interface"]}
rustboyadvance-utils = {path = "../../utils/" }
rustboyadvance-core = { path = "../../core/" }
rustboyadvance-utils = { path = "../../utils/" }
jni = "0.17.0"
log = {version = "0.4.8", features = ["release_max_level_info", "max_level_debug"]}

View file

@ -15,12 +15,12 @@ use jni::JNIEnv;
use crate::audio::{self, connector::AudioJNIConnector, thread::AudioThreadCommand};
struct Hardware {
struct AudioDevice {
sample_rate: i32,
audio_producer: Option<Producer<i16>>,
}
impl AudioInterface for Hardware {
impl AudioInterface for AudioDevice {
fn push_sample(&mut self, samples: &[i16]) {
if let Some(prod) = &mut self.audio_producer {
for s in samples.iter() {
@ -146,7 +146,7 @@ impl Default for EmulationState {
}
pub struct EmulatorContext {
hwif: Rc<RefCell<Hardware>>,
audio_device: Rc<RefCell<AudioDevice>>,
renderer: Renderer,
audio_player_ref: GlobalRef,
keypad: Keypad,
@ -188,11 +188,11 @@ impl EmulatorContext {
let renderer = Renderer::new(env, renderer_obj)?;
info!("Creating GBA Instance");
let hw = Rc::new(RefCell::new(Hardware {
let audio = Rc::new(RefCell::new(AudioDevice {
sample_rate: audio::util::get_sample_rate(env, audio_player),
audio_producer: None,
}));
let mut gba = GameBoyAdvance::new(bios, gamepak, hw.clone());
let mut gba = GameBoyAdvance::new(bios, gamepak, audio.clone());
if skip_bios != 0 {
info!("skipping bios");
gba.skip_bios();
@ -209,7 +209,7 @@ impl EmulatorContext {
renderer,
audio_player_ref,
emustate: Mutex::new(EmulationState::default()),
hwif: hw.clone(),
audio_device: audio.clone(),
};
Ok(context)
}
@ -237,17 +237,18 @@ impl EmulatorContext {
let renderer = Renderer::new(env, renderer_obj)?;
let hw = Rc::new(RefCell::new(Hardware {
let audio = Rc::new(RefCell::new(AudioDevice {
sample_rate: audio::util::get_sample_rate(env, audio_player),
audio_producer: None,
}));
let gba = GameBoyAdvance::from_saved_state(&savestate, bios, rom, hw.clone())
.map_err(|e| {
let gba = GameBoyAdvance::from_saved_state(&savestate, bios, rom, audio.clone()).map_err(
|e| {
format!(
"failed to create GameBoyAdvance from saved savestate, error {:?}",
e
)
})?;
},
)?;
let keypad = Keypad::new(env, keypad_obj);
@ -258,7 +259,7 @@ impl EmulatorContext {
renderer,
audio_player_ref,
emustate: Mutex::new(EmulationState::default()),
hwif: hw.clone(),
audio_device: audio.clone(),
})
}
@ -288,7 +289,7 @@ impl EmulatorContext {
let (prod, cons) = AudioRingBuffer::new_with_capacity(audio_connector.sample_count).split();
// Store the ringbuffer producer in the emulator
self.hwif.borrow_mut().audio_producer = Some(prod);
self.audio_device.borrow_mut().audio_producer = Some(prod);
// Spawn the audio worker thread, give it the audio connector, jvm and ringbuffer consumer
let (audio_thread_handle, audio_thread_tx) =
@ -354,7 +355,7 @@ impl EmulatorContext {
audio_connector.pause(env);
self.hwif.borrow_mut().audio_producer = None;
self.audio_device.borrow_mut().audio_producer = None;
*self.emustate.lock().unwrap() = EmulationState::Stopped;

View file

@ -10,7 +10,7 @@ crate-type = ["cdylib"]
[dependencies]
rustboyadvance-core = { path = "../../core/", features = ["no_video_interface"] }
rustboyadvance-core = { path = "../../core/" }
rustboyadvance-utils = { path = "../../utils" }
log = "0.4.8"
libc = "0.2"

View file

@ -49,7 +49,7 @@ struct GbaButton(_GbaButton);
impl Deref for GbaButton {
type Target = _GbaButton;
fn deref(&self) -> &Self::Target {
return &self.0
return &self.0;
}
}

View file

@ -1,4 +1,3 @@
use sdl2;
use sdl2::controller::Button;
use sdl2::event::{Event, WindowEvent};
use sdl2::image::{InitFlag, LoadSurface, LoadTexture};
@ -7,6 +6,7 @@ use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::WindowCanvas;
use sdl2::surface::Surface;
use sdl2::{self};
use sdl2::EventPump;
@ -37,7 +37,7 @@ mod input;
mod video;
use audio::{create_audio_player, create_dummy_player};
use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH};
use video::{SCREEN_HEIGHT, SCREEN_WIDTH};
use rustboyadvance_core::cartridge::BackupType;
use rustboyadvance_core::prelude::*;
@ -182,7 +182,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
};
let video = Rc::new(RefCell::new(create_video_interface(canvas)));
let mut renderer = video::Renderer::from_canvas(canvas);
let audio: Rc<RefCell<dyn AudioInterface>> = if silent {
Rc::new(RefCell::new(create_dummy_player()))
} else {
@ -205,12 +205,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let gamepak = builder.build()?;
let mut gba = GameBoyAdvance::new(
bios_bin.clone(),
gamepak,
video.clone(),
audio.clone(),
);
let mut gba = GameBoyAdvance::new(bios_bin.clone(), gamepak, audio.clone());
if skip_bios {
gba.skip_bios();
@ -331,12 +326,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let bios_bin = read_bin_file(bios_path).unwrap();
// create a new emulator - TODO, export to a function
gba = GameBoyAdvance::new(
bios_bin.into_boxed_slice(),
gamepak,
video.clone(),
audio.clone(),
);
gba = GameBoyAdvance::new(bios_bin.into_boxed_slice(), gamepak, audio.clone());
gba.skip_bios();
}
_ => {}
@ -344,10 +334,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
gba.frame();
renderer.render(gba.get_frame_buffer());
if let Some(fps) = fps_counter.tick() {
let title = format!("{} ({} fps)", rom_name, fps);
video.borrow_mut().set_window_title(&title);
renderer.set_window_title(&title);
}
if frame_limiter {

View file

@ -4,25 +4,37 @@ use sdl2::render::{Texture, TextureCreator, WindowCanvas};
use sdl2::video::WindowContext;
use rustboyadvance_core::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
use rustboyadvance_core::VideoInterface;
pub const SCREEN_WIDTH: u32 = DISPLAY_WIDTH as u32;
pub const SCREEN_HEIGHT: u32 = DISPLAY_HEIGHT as u32;
pub struct Sdl2Video<'a> {
pub struct Renderer<'a> {
_tc: TextureCreator<WindowContext>, // only kept alive because of the texture
texture: Texture<'a>, // TODO - what happens if _tc is destroyed first ?
canvas: WindowCanvas,
}
impl<'a> Sdl2Video<'a> {
impl<'a> Renderer<'a> {
pub fn from_canvas(canvas: WindowCanvas) -> Renderer<'a> {
let mut tc = canvas.texture_creator();
let texture = unsafe {
let tc_ptr = &mut tc as *mut TextureCreator<WindowContext>;
(*tc_ptr)
.create_texture_streaming(PixelFormatEnum::BGRA32, SCREEN_WIDTH, SCREEN_HEIGHT)
.unwrap()
};
Renderer {
_tc: tc,
texture,
canvas,
}
}
pub fn set_window_title(&mut self, title: &str) {
self.canvas.window_mut().set_title(&title).unwrap();
}
}
impl<'a> VideoInterface for Sdl2Video<'a> {
fn render(&mut self, buffer: &[u32]) {
pub fn render(&mut self, buffer: &[u32]) {
self.texture
.update(
None,
@ -42,18 +54,3 @@ impl<'a> VideoInterface for Sdl2Video<'a> {
self.canvas.present();
}
}
pub fn create_video_interface<'a>(canvas: WindowCanvas) -> Sdl2Video<'a> {
let mut tc = canvas.texture_creator();
let texture = unsafe {
let tc_ptr = &mut tc as *mut TextureCreator<WindowContext>;
(*tc_ptr)
.create_texture_streaming(PixelFormatEnum::BGRA32, SCREEN_WIDTH, SCREEN_HEIGHT)
.unwrap()
};
Sdl2Video {
_tc: tc,
texture: texture,
canvas: canvas,
}
}

View file

@ -19,10 +19,21 @@ use bit::BitIndex;
pub struct Emulator {
gba: GameBoyAdvance,
interface: Rc<RefCell<Interface>>,
frame: Option<Box<[u8]>>,
}
fn translate_frame_to_u8(input_fb: &[u32], out_fb: &mut [u8]) {
// TODO optimize
for i in 0..input_fb.len() {
let color = input_fb[i];
out_fb[4 * i + 0] = ((color >> 16) & 0xff) as u8;
out_fb[4 * i + 1] = ((color >> 8) & 0xff) as u8;
out_fb[4 * i + 2] = (color & 0xff) as u8;
out_fb[4 * i + 3] = 255;
}
}
struct Interface {
frame: Vec<u8>,
sample_rate: i32,
audio_ctx: AudioContext,
audio_ring_buffer: AudioRingBuffer,
@ -37,7 +48,6 @@ impl Drop for Interface {
impl Interface {
fn new(audio_ctx: AudioContext) -> Result<Interface, JsValue> {
Ok(Interface {
frame: vec![0; 240 * 160 * 4],
sample_rate: audio_ctx.sample_rate() as i32,
audio_ctx: audio_ctx,
audio_ring_buffer: Default::default(),
@ -45,19 +55,6 @@ impl Interface {
}
}
impl VideoInterface for Interface {
fn render(&mut self, buffer: &[u32]) {
// TODO optimize
for i in 0..buffer.len() {
let color = buffer[i];
self.frame[4 * i + 0] = ((color >> 16) & 0xff) as u8;
self.frame[4 * i + 1] = ((color >> 8) & 0xff) as u8;
self.frame[4 * i + 2] = (color & 0xff) as u8;
self.frame[4 * i + 3] = 255;
}
}
}
fn convert_sample(s: i16) -> f32 {
(s as f32) / 32767_f32
}
@ -88,15 +85,13 @@ impl Emulator {
.build()
.unwrap();
let gba = GameBoyAdvance::new(
bios.to_vec().into_boxed_slice(),
gamepak,
interface.clone(),
interface.clone(),
interface.clone(),
);
let gba = GameBoyAdvance::new(bios.to_vec().into_boxed_slice(), gamepak, interface.clone());
Ok(Emulator { gba, interface })
Ok(Emulator {
gba,
interface,
frame: Some(vec![0; 240 * 160 * 4].into_boxed_slice()),
})
}
pub fn skip_bios(&mut self) {
@ -105,13 +100,11 @@ impl Emulator {
pub fn run_frame(&mut self, ctx: &CanvasRenderingContext2d) -> Result<(), JsValue> {
self.gba.frame();
let mut frame_buffer = &mut self.interface.borrow_mut().frame;
let data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(
Clamped(&mut frame_buffer),
240,
160,
)
.unwrap();
let mut frame = self.frame.take().unwrap();
translate_frame_to_u8(self.gba.get_frame_buffer(), &mut frame);
let data =
web_sys::ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut frame), 240, 160)?;
self.frame.replace(frame);
ctx.put_image_data(&data, 0.0, 0.0)
}
@ -134,14 +127,14 @@ impl Emulator {
pub fn key_down(&mut self, event_key: &str) {
debug!("Key down: {}", event_key);
if let Some(key) = Emulator::map_key(event_key) {
self.gba.get_key_state().set_bit(key as usize, false);
self.gba.get_key_state_mut().set_bit(key as usize, false);
}
}
pub fn key_up(&mut self, event_key: &str) {
debug!("Key up: {}", event_key);
if let Some(key) = Emulator::map_key(event_key) {
self.gba.get_key_state().set_bit(key as usize, true);
self.gba.get_key_state_mut().set_bit(key as usize, true);
}
}