2019-06-30 20:31:16 +01:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io;
|
|
|
|
use std::io::prelude::*;
|
2020-05-29 15:39:35 +01:00
|
|
|
use std::ops::{Deref, DerefMut};
|
2019-12-04 23:15:49 +00:00
|
|
|
use std::path::Path;
|
2020-05-29 15:39:35 +01:00
|
|
|
use std::ptr;
|
2019-12-04 23:15:49 +00:00
|
|
|
use std::time;
|
2019-06-30 20:31:16 +01:00
|
|
|
|
2020-05-29 15:54:07 +01:00
|
|
|
use super::bus::{Addr, Bus, DebugRead};
|
2020-05-29 15:46:44 +01:00
|
|
|
|
2020-04-13 15:55:39 +01:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
type Instant = time::Instant;
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
fn now() -> Instant {
|
|
|
|
time::Instant::now()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
use instant;
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
type Instant = instant::Instant;
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
fn now() -> Instant {
|
|
|
|
instant::Instant::now()
|
|
|
|
}
|
|
|
|
|
2020-05-01 16:13:00 +01:00
|
|
|
use crate::GameBoyAdvance;
|
2020-02-22 22:19:55 +00:00
|
|
|
#[cfg(feature = "gdb")]
|
|
|
|
use gdbstub;
|
|
|
|
#[cfg(feature = "gdb")]
|
|
|
|
use gdbstub::GdbStub;
|
|
|
|
use std::fmt;
|
|
|
|
#[cfg(feature = "gdb")]
|
|
|
|
use std::net::TcpListener;
|
2020-02-23 21:05:09 +00:00
|
|
|
use std::net::ToSocketAddrs;
|
2020-02-22 22:19:55 +00:00
|
|
|
|
|
|
|
pub fn spawn_and_run_gdb_server<A: ToSocketAddrs + fmt::Display>(
|
2020-05-01 16:28:49 +01:00
|
|
|
#[allow(unused)] target: &mut GameBoyAdvance,
|
|
|
|
#[allow(unused)] addr: A,
|
2020-02-22 22:19:55 +00:00
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
#[cfg(feature = "gdb")]
|
|
|
|
{
|
|
|
|
info!("spawning gdbserver, listening on {}", addr);
|
|
|
|
|
|
|
|
let sock = TcpListener::bind(addr)?;
|
|
|
|
let (stream, addr) = sock.accept()?;
|
|
|
|
|
|
|
|
info!("got connection from {}", addr);
|
|
|
|
|
|
|
|
let mut gdb = GdbStub::new(stream);
|
|
|
|
let result = match gdb.run(target) {
|
|
|
|
Ok(state) => {
|
|
|
|
info!("Disconnected from GDB. Target state: {:?}", state);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(gdbstub::Error::TargetError(e)) => Err(e),
|
|
|
|
Err(e) => return Err(e.into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
info!("Debugger session ended, result={:?}", result);
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "gdb"))]
|
|
|
|
{
|
|
|
|
error!("failed. please compile me with 'gdb' feature")
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-04 23:15:49 +00:00
|
|
|
pub fn read_bin_file(filename: &Path) -> io::Result<Vec<u8>> {
|
2019-06-30 20:31:16 +01:00
|
|
|
let mut buf = Vec::new();
|
|
|
|
let mut file = File::open(filename)?;
|
|
|
|
file.read_to_end(&mut buf)?;
|
|
|
|
Ok(buf)
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-07-30 22:52:46 +01:00
|
|
|
|
2020-01-16 17:49:43 +00:00
|
|
|
pub fn write_bin_file(filename: &Path, data: &Vec<u8>) -> io::Result<()> {
|
|
|
|
let mut f = File::create(filename)?;
|
|
|
|
f.write_all(data)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-04 23:15:49 +00:00
|
|
|
pub struct FpsCounter {
|
|
|
|
count: u32,
|
2020-04-13 15:55:39 +01:00
|
|
|
timer: Instant,
|
2019-12-04 23:15:49 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 14:29:56 +01:00
|
|
|
const SECOND: time::Duration = time::Duration::from_secs(1);
|
|
|
|
|
2019-12-04 23:15:49 +00:00
|
|
|
impl Default for FpsCounter {
|
|
|
|
fn default() -> FpsCounter {
|
|
|
|
FpsCounter {
|
|
|
|
count: 0,
|
2020-04-13 15:55:39 +01:00
|
|
|
timer: now(),
|
2019-12-04 23:15:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FpsCounter {
|
|
|
|
pub fn tick(&mut self) -> Option<u32> {
|
|
|
|
self.count += 1;
|
2020-04-11 14:29:56 +01:00
|
|
|
if self.timer.elapsed() >= SECOND {
|
2019-12-04 23:15:49 +00:00
|
|
|
let fps = self.count;
|
2020-04-13 15:55:39 +01:00
|
|
|
self.timer = now();
|
2019-12-04 23:15:49 +00:00
|
|
|
self.count = 0;
|
|
|
|
Some(fps)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 22:52:46 +01:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! index2d {
|
|
|
|
($x:expr, $y:expr, $w:expr) => {
|
|
|
|
$w * $y + $x
|
|
|
|
};
|
2019-08-13 19:57:45 +01:00
|
|
|
($t:ty, $x:expr, $y:expr, $w:expr) => {
|
|
|
|
(($w as $t) * ($y as $t) + ($x as $t)) as $t
|
|
|
|
};
|
2019-07-30 22:52:46 +01:00
|
|
|
}
|
2019-11-11 01:35:16 +00:00
|
|
|
|
2020-02-14 12:01:48 +00:00
|
|
|
#[allow(unused_macros)]
|
2019-11-11 01:35:16 +00:00
|
|
|
macro_rules! host_breakpoint {
|
|
|
|
() => {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
unsafe {
|
|
|
|
::std::intrinsics::breakpoint()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2020-04-15 13:32:48 +01:00
|
|
|
|
|
|
|
pub mod audio {
|
|
|
|
use ringbuf::{Consumer, Producer, RingBuffer};
|
|
|
|
|
|
|
|
pub struct AudioRingBuffer {
|
|
|
|
pub prod: Producer<i16>,
|
|
|
|
pub cons: Consumer<i16>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AudioRingBuffer {
|
|
|
|
pub fn new() -> AudioRingBuffer {
|
|
|
|
let rb = RingBuffer::new(4096 * 2);
|
|
|
|
let (prod, cons) = rb.split();
|
|
|
|
|
|
|
|
AudioRingBuffer { prod, cons }
|
|
|
|
}
|
2020-05-15 11:22:58 +01:00
|
|
|
|
|
|
|
pub fn producer(&mut self) -> &mut Producer<i16> {
|
|
|
|
&mut self.prod
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn consumer(&mut self) -> &mut Consumer<i16> {
|
|
|
|
&mut self.cons
|
|
|
|
}
|
2020-04-15 13:32:48 +01:00
|
|
|
}
|
2020-05-01 15:58:15 +01:00
|
|
|
}
|
2020-05-29 15:39:35 +01:00
|
|
|
|
|
|
|
#[repr(transparent)]
|
|
|
|
#[derive(Clone)]
|
|
|
|
/// Wrapper for passing raw pointers around.
|
|
|
|
/// Breaks compiler safety guaranties, so must be used with care.
|
|
|
|
pub struct WeakPointer<T: ?Sized> {
|
|
|
|
ptr: *mut T,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> WeakPointer<T> {
|
|
|
|
pub fn new(ptr: *mut T) -> Self {
|
|
|
|
WeakPointer { ptr }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Deref for WeakPointer<T> {
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
fn deref(&self) -> &T {
|
|
|
|
unsafe { &(*self.ptr) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> DerefMut for WeakPointer<T> {
|
|
|
|
fn deref_mut(&mut self) -> &mut T {
|
|
|
|
unsafe { &mut (*self.ptr) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Default for WeakPointer<T> {
|
|
|
|
fn default() -> Self {
|
|
|
|
WeakPointer {
|
|
|
|
ptr: ptr::null_mut(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-29 15:46:44 +01:00
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
|
|
#[repr(transparent)]
|
|
|
|
pub struct BoxedMemory {
|
|
|
|
pub mem: Box<[u8]>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BoxedMemory {
|
|
|
|
pub fn new(boxed_slice: Box<[u8]>) -> BoxedMemory {
|
|
|
|
BoxedMemory { mem: boxed_slice }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bus for BoxedMemory {
|
|
|
|
fn read_8(&self, addr: Addr) -> u8 {
|
|
|
|
unsafe { *self.mem.get_unchecked(addr as usize) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
|
|
|
unsafe {
|
|
|
|
*self.mem.get_unchecked_mut(addr as usize) = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-29 15:54:07 +01:00
|
|
|
|
|
|
|
impl DebugRead for BoxedMemory {
|
|
|
|
fn debug_read_8(&self, addr: Addr) -> u8 {
|
|
|
|
self.mem[addr as usize]
|
|
|
|
}
|
|
|
|
}
|