mirror of
https://github.com/koute/pinky.git
synced 2024-06-12 17:27:48 -04:00
Add workspace and #[no_std]
support
This commit is contained in:
parent
17c51a1e96
commit
59c2a652db
18
.gitignore
vendored
18
.gitignore
vendored
|
@ -1,16 +1,2 @@
|
||||||
pinky-devui/target
|
Cargo.toml
|
||||||
pinky-devui/Cargo.lock
|
Cargo.lock
|
||||||
pinky-libretro/target
|
|
||||||
pinky-libretro/Cargo.lock
|
|
||||||
pinky-web/target
|
|
||||||
pinky-web/Cargo.lock
|
|
||||||
nes-testsuite/target
|
|
||||||
nes-testsuite/Cargo.lock
|
|
||||||
mos6502/target
|
|
||||||
mos6502/Cargo.lock
|
|
||||||
nes/target
|
|
||||||
nes/Cargo.lock
|
|
||||||
emumisc/target
|
|
||||||
emumisc/Cargo.lock
|
|
||||||
rp2c02-testsuite/target
|
|
||||||
rp2c02-testsuite/Cargo.lock
|
|
||||||
|
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"emumisc",
|
||||||
|
"mos6502",
|
||||||
|
"nes",
|
||||||
|
"nes-testsuite",
|
||||||
|
"pinky-devui",
|
||||||
|
"pinky-libretro",
|
||||||
|
"pinky-web",
|
||||||
|
"rp2c02-testsuite"
|
||||||
|
]
|
||||||
|
|
||||||
|
[profile.test]
|
||||||
|
opt-level = 2
|
|
@ -1,4 +1,4 @@
|
||||||
use std::mem;
|
use core::mem;
|
||||||
|
|
||||||
#[inline(always)] pub fn is_b0_set( value: u8 ) -> bool { (value & (1 << 0)) != 0 }
|
#[inline(always)] pub fn is_b0_set( value: u8 ) -> bool { (value & (1 << 0)) != 0 }
|
||||||
#[inline(always)] pub fn is_b1_set( value: u8 ) -> bool { (value & (1 << 1)) != 0 }
|
#[inline(always)] pub fn is_b1_set( value: u8 ) -> bool { (value & (1 << 1)) != 0 }
|
||||||
|
@ -11,8 +11,6 @@ use std::mem;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_bit( bit: u8, value: bool ) -> u8 {
|
pub fn to_bit( bit: u8, value: bool ) -> u8 {
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
debug_assert!( bit < 8 );
|
debug_assert!( bit < 8 );
|
||||||
let converted: u8 = unsafe { mem::transmute( value ) };
|
let converted: u8 = unsafe { mem::transmute( value ) };
|
||||||
debug_assert!( converted == 0 || converted == 1 );
|
debug_assert!( converted == 0 || converted == 1 );
|
||||||
|
@ -37,7 +35,7 @@ macro_rules! impl_bit_extra {
|
||||||
impl BitExtra for $typ {
|
impl BitExtra for $typ {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mask_to_shift( mask: Self ) -> u8 {
|
fn mask_to_shift( mask: Self ) -> u8 {
|
||||||
use std::mem::size_of;
|
use core::mem::size_of;
|
||||||
debug_assert!( mask != 0 );
|
debug_assert!( mask != 0 );
|
||||||
|
|
||||||
for n_bit in 0..(size_of::< Self >() * 8) as u8 {
|
for n_bit in 0..(size_of::< Self >() * 8) as u8 {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
extern crate unreachable;
|
extern crate unreachable;
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod misc;
|
mod misc;
|
||||||
|
@ -31,7 +34,7 @@ pub use memory::{as_bytes, allocate_slice, copy_memory};
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_deref {
|
macro_rules! impl_deref {
|
||||||
($container: ty, $field_type: ty, $field: ident) => (
|
($container: ty, $field_type: ty, $field: ident) => (
|
||||||
impl ::std::ops::Deref for $container {
|
impl ::core::ops::Deref for $container {
|
||||||
type Target = $field_type;
|
type Target = $field_type;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -40,7 +43,7 @@ macro_rules! impl_deref {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::ops::DerefMut for $container {
|
impl ::core::ops::DerefMut for $container {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref_mut( &mut self ) -> &mut Self::Target {
|
fn deref_mut( &mut self ) -> &mut Self::Target {
|
||||||
&mut self.$field
|
&mut self.$field
|
||||||
|
@ -52,14 +55,14 @@ macro_rules! impl_deref {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_as_ref {
|
macro_rules! impl_as_ref {
|
||||||
($container: ty, $field_type: ty, $field: ident) => (
|
($container: ty, $field_type: ty, $field: ident) => (
|
||||||
impl ::std::convert::AsRef< $field_type > for $container {
|
impl ::core::convert::AsRef< $field_type > for $container {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref( &self ) -> &$field_type {
|
fn as_ref( &self ) -> &$field_type {
|
||||||
&self.$field
|
&self.$field
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::convert::AsMut< $field_type > for $container {
|
impl ::core::convert::AsMut< $field_type > for $container {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mut( &mut self ) -> &mut $field_type {
|
fn as_mut( &mut self ) -> &mut $field_type {
|
||||||
&mut self.$field
|
&mut self.$field
|
||||||
|
@ -74,7 +77,7 @@ macro_rules! newtype {
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub struct $new( pub $old );
|
pub struct $new( pub $old );
|
||||||
|
|
||||||
impl ::std::ops::Deref for $new {
|
impl ::core::ops::Deref for $new {
|
||||||
type Target = $old;
|
type Target = $old;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -83,21 +86,21 @@ macro_rules! newtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::ops::DerefMut for $new {
|
impl ::core::ops::DerefMut for $new {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref_mut( &mut self ) -> &mut Self::Target {
|
fn deref_mut( &mut self ) -> &mut Self::Target {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::convert::AsRef< $old > for $new {
|
impl ::core::convert::AsRef< $old > for $new {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref( &self ) -> &$old {
|
fn as_ref( &self ) -> &$old {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::convert::AsMut< $old > for $new {
|
impl ::core::convert::AsMut< $old > for $new {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mut( &mut self ) -> &mut $old {
|
fn as_mut( &mut self ) -> &mut $old {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::ptr;
|
use core::ptr;
|
||||||
use std::slice;
|
use core::slice;
|
||||||
use std::mem;
|
use core::mem;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes< T: Copy >( array: &[T] ) -> &[u8] {
|
pub fn as_bytes< T: Copy >( array: &[T] ) -> &[u8] {
|
||||||
|
|
|
@ -9,3 +9,6 @@ bitflags = "0.7"
|
||||||
[dependencies.emumisc]
|
[dependencies.emumisc]
|
||||||
path = "../emumisc"
|
path = "../emumisc"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
|
|
|
@ -345,9 +345,8 @@ def generate_opcode_attributes
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_register( &self ) -> Register8 {
|
fn get_register( &self ) -> Register8 {
|
||||||
use std::mem;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
mem::transmute( (self.attr & 0b00011000) >> 3 )
|
core::mem::transmute( (self.attr & 0b00011000) >> 3 )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
#![cfg_attr(not(feature = "std"), feature(error_in_core))]
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
extern crate std as core;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate emumisc;
|
extern crate emumisc;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use self::Register8::*;
|
use self::Register8::*;
|
||||||
use self::Direction::*;
|
use self::Direction::*;
|
||||||
|
|
||||||
use std::mem::transmute;
|
use core::mem::transmute;
|
||||||
use std::fmt;
|
use core::fmt;
|
||||||
use std::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::error;
|
|
||||||
|
|
||||||
use emumisc::WrappingExtra;
|
use emumisc::WrappingExtra;
|
||||||
|
|
||||||
|
@ -227,7 +226,7 @@ impl fmt::Display for EmulationError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for EmulationError {
|
impl core::error::Error for EmulationError {
|
||||||
fn description( &self ) -> &str {
|
fn description( &self ) -> &str {
|
||||||
use self::EmulationError::*;
|
use self::EmulationError::*;
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -1018,6 +1017,7 @@ trait Private: Sized + Context {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
fn print_registers( &self ) {
|
fn print_registers( &self ) {
|
||||||
println!( " NV-BDIZC A X Y PC" );
|
println!( " NV-BDIZC A X Y PC" );
|
||||||
println!( " {:08b} {:02X} {:02X} {:02X} {:04X}", self.state().status, self.state().a, self.state().x, self.state().y, self.state().pc );
|
println!( " {:08b} {:02X} {:02X} {:02X} {:04X}", self.state().status, self.state().a, self.state().x, self.state().y, self.state().pc );
|
||||||
|
|
|
@ -761,9 +761,8 @@ macro_rules! decoding_logic {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_register( &self ) -> Register8 {
|
fn get_register( &self ) -> Register8 {
|
||||||
use std::mem;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
mem::transmute( (self.attr & 0b00011000) >> 3 )
|
core::mem::transmute( (self.attr & 0b00011000) >> 3 )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ version = "0.1.0"
|
||||||
authors = ["Jan Bujak <j@exia.io>"]
|
authors = ["Jan Bujak <j@exia.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "0.5"
|
log = { version = "0.4", default-features = false, optional = true }
|
||||||
log = "0.3"
|
|
||||||
bitflags = "0.7"
|
bitflags = "0.7"
|
||||||
|
|
||||||
[dependencies.emumisc]
|
[dependencies.emumisc]
|
||||||
|
@ -13,6 +12,7 @@ path = "../emumisc"
|
||||||
|
|
||||||
[dependencies.mos6502]
|
[dependencies.mos6502]
|
||||||
path = "../mos6502"
|
path = "../mos6502"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies.nes-testsuite]
|
[dev-dependencies.nes-testsuite]
|
||||||
path = "../nes-testsuite"
|
path = "../nes-testsuite"
|
||||||
|
@ -20,10 +20,6 @@ path = "../nes-testsuite"
|
||||||
[dev-dependencies.rp2c02-testsuite]
|
[dev-dependencies.rp2c02-testsuite]
|
||||||
path = "../rp2c02-testsuite"
|
path = "../rp2c02-testsuite"
|
||||||
|
|
||||||
[profile.test]
|
[features]
|
||||||
opt-level = 2
|
default = ["std"]
|
||||||
debug = true
|
std = ["mos6502/std", "log", "log/std"]
|
||||||
rpath = false
|
|
||||||
lto = false
|
|
||||||
debug-assertions = true
|
|
||||||
codegen-units = 4
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use std::fmt;
|
use core::fmt;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use emumisc::{PeekPoke, At};
|
use emumisc::{PeekPoke, At};
|
||||||
use rom::Mirroring;
|
use rom::Mirroring;
|
||||||
use mappers::Mapper;
|
use mappers::Mapper;
|
||||||
|
@ -464,6 +466,7 @@ impl GenericMapper {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn poke_cpu_memory_space( &mut self, address: u16, value: u8 ) {
|
pub fn poke_cpu_memory_space( &mut self, address: u16, value: u8 ) {
|
||||||
if self.is_cpu_address_writable( address ) == false {
|
if self.is_cpu_address_writable( address ) == false {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled write to 0x{:04X} (value=0x{:02X})", address, value );
|
warn!( "Unhandled write to 0x{:04X} (value=0x{:02X})", address, value );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -523,7 +526,7 @@ impl Mapper for GenericMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::ops::Sub;
|
use core::ops::Sub;
|
||||||
use rom::{NesRom, LoadError};
|
use rom::{NesRom, LoadError};
|
||||||
#[inline]
|
#[inline]
|
||||||
fn wraparound< T: Sub< Output = T > + PartialOrd + Copy >( limit: T, mut value: T ) -> T {
|
fn wraparound< T: Sub< Output = T > + PartialOrd + Copy >( limit: T, mut value: T ) -> T {
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
#![cfg_attr(not(feature = "std"), feature(error_in_core))]
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(missing_copy_implementations)]
|
#![allow(missing_copy_implementations)]
|
||||||
|
|
||||||
extern crate byteorder;
|
#[cfg(feature = "std")]
|
||||||
|
extern crate std as core;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
extern crate std as alloc;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ impl Mapper for MapperMMC1 {
|
||||||
_ => unsafe { fast_unreachable!() }
|
_ => unsafe { fast_unreachable!() }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
debug!( "ROM switching mode = {:?}, VROM switching mode = {:?}",
|
debug!( "ROM switching mode = {:?}, VROM switching mode = {:?}",
|
||||||
self.rom_switching_mode,
|
self.rom_switching_mode,
|
||||||
self.vrom_switching_mode
|
self.vrom_switching_mode
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::format;
|
||||||
|
|
||||||
use rom::{NesRom, LoadError};
|
use rom::{NesRom, LoadError};
|
||||||
use generic_mapper::GenericMapper;
|
use generic_mapper::GenericMapper;
|
||||||
use mapper_mmc1::MapperMMC1;
|
use mapper_mmc1::MapperMMC1;
|
||||||
|
@ -10,20 +13,24 @@ pub trait Mapper {
|
||||||
fn poke_rom( &mut self, address: u16, value: u8 );
|
fn poke_rom( &mut self, address: u16, value: u8 );
|
||||||
|
|
||||||
fn peek_sram( &self, address: u16 ) -> u8 {
|
fn peek_sram( &self, address: u16 ) -> u8 {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled read from the save RAM at 0x{:04X}", address );
|
warn!( "Unhandled read from the save RAM at 0x{:04X}", address );
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poke_sram( &mut self, address: u16, value: u8 ) {
|
fn poke_sram( &mut self, address: u16, value: u8 ) {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled write to the save RAM at 0x{:04X} (value=0x{:02X})", address, value );
|
warn!( "Unhandled write to the save RAM at 0x{:04X} (value=0x{:02X})", address, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_expansion_rom( &self, address: u16 ) -> u8 {
|
fn peek_expansion_rom( &self, address: u16 ) -> u8 {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled read from the expansion ROM at 0x{:04X}", address );
|
warn!( "Unhandled read from the expansion ROM at 0x{:04X}", address );
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poke_expansion_rom( &self, address: u16, value: u8 ) {
|
fn poke_expansion_rom( &self, address: u16, value: u8 ) {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled write to the expansion ROM at 0x{:04X} (value=0x{:02X})", address, value );
|
warn!( "Unhandled write to the expansion ROM at 0x{:04X} (value=0x{:02X})", address, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,20 +42,24 @@ pub struct MapperNull;
|
||||||
|
|
||||||
impl Mapper for MapperNull {
|
impl Mapper for MapperNull {
|
||||||
fn peek_rom( &self, address: u16 ) -> u8 {
|
fn peek_rom( &self, address: u16 ) -> u8 {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled read from the ROM at 0x{:04X}", address );
|
warn!( "Unhandled read from the ROM at 0x{:04X}", address );
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poke_rom( &mut self, address: u16, value: u8 ) {
|
fn poke_rom( &mut self, address: u16, value: u8 ) {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled write to the ROM at 0x{:04X} (value=0x{:02X})", address, value );
|
warn!( "Unhandled write to the ROM at 0x{:04X} (value=0x{:02X})", address, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_video_memory( &self, address: u16 ) -> u8 {
|
fn peek_video_memory( &self, address: u16 ) -> u8 {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled read from the VROM at 0x{:04X}", address );
|
warn!( "Unhandled read from the VROM at 0x{:04X}", address );
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poke_video_memory( &mut self, address: u16, value: u8 ) {
|
fn poke_video_memory( &mut self, address: u16, value: u8 ) {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled write to the VROM at 0x{:04X} (value=0x{:02X})", address, value );
|
warn!( "Unhandled write to the VROM at 0x{:04X} (value=0x{:02X})", address, value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +76,7 @@ pub fn create_mapper( rom: NesRom ) -> Result< Box< Mapper >, LoadError > {
|
||||||
mapper.initialize_video_rom( &rom.video_rom[..] );
|
mapper.initialize_video_rom( &rom.video_rom[..] );
|
||||||
mapper.initialize_background_tilemaps( rom.mirroring );
|
mapper.initialize_background_tilemaps( rom.mirroring );
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
debug!( "Initialized mapper: {:?}", mapper );
|
debug!( "Initialized mapper: {:?}", mapper );
|
||||||
Ok( Box::new( mapper ) )
|
Ok( Box::new( mapper ) )
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::mem;
|
use core::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
// A shim like this is necessary to implement an orphaned instance in Rust.
|
// A shim like this is necessary to implement an orphaned instance in Rust.
|
||||||
// It's very important to keep the structure opaque to keep it safe to use.
|
// It's very important to keep the structure opaque to keep it safe to use.
|
||||||
|
|
|
@ -1,23 +1,9 @@
|
||||||
use std::io;
|
use core::cmp::max;
|
||||||
use std::io::{Read, Error, ErrorKind, Seek, SeekFrom};
|
use core::fmt;
|
||||||
use std::cmp::max;
|
use core::error;
|
||||||
use std::fmt;
|
use alloc::string::String;
|
||||||
use std::error;
|
use alloc::vec::Vec;
|
||||||
|
use alloc::format;
|
||||||
use byteorder::{ReadBytesExt, LittleEndian};
|
|
||||||
|
|
||||||
fn fill_array< T: Read >( fp: &mut T, out: &mut [u8] ) -> io::Result<()> {
|
|
||||||
match fp.read( out ) {
|
|
||||||
Ok( size ) => {
|
|
||||||
if size == out.len() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err( Error::new( ErrorKind::Other, "Unexpected end of file found" ) )
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err( error ) => Err( error )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decapitalize< M: fmt::Display >( msg: M ) -> String {
|
fn decapitalize< M: fmt::Display >( msg: M ) -> String {
|
||||||
let message = format!( "{}", msg );
|
let message = format!( "{}", msg );
|
||||||
|
@ -32,7 +18,6 @@ fn decapitalize< M: fmt::Display >( msg: M ) -> String {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LoadError {
|
pub enum LoadError {
|
||||||
Custom( String ),
|
Custom( String ),
|
||||||
IO( io::Error )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoadError {
|
impl LoadError {
|
||||||
|
@ -45,7 +30,6 @@ impl fmt::Display for LoadError {
|
||||||
fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
|
fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
LoadError::Custom( ref message ) => try!( write!( fmt, "Unable to load ROM - {}", decapitalize( message ))),
|
LoadError::Custom( ref message ) => try!( write!( fmt, "Unable to load ROM - {}", decapitalize( message ))),
|
||||||
LoadError::IO( ref error ) => try!( write!( fmt, "Unable to load ROM - {}", decapitalize( error ))),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -56,17 +40,10 @@ impl error::Error for LoadError {
|
||||||
fn description( &self ) -> &str {
|
fn description( &self ) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
LoadError::Custom( ref message ) => &message[..],
|
LoadError::Custom( ref message ) => &message[..],
|
||||||
LoadError::IO( ref error ) => error.description()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From< io::Error > for LoadError {
|
|
||||||
fn from( error: io::Error ) -> LoadError {
|
|
||||||
LoadError::IO( error )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const ROM_BANK_SIZE: usize = 16 * 1024;
|
pub const ROM_BANK_SIZE: usize = 16 * 1024;
|
||||||
pub const VROM_BANK_SIZE: usize = 8 * 1024;
|
pub const VROM_BANK_SIZE: usize = 8 * 1024;
|
||||||
|
|
||||||
|
@ -100,23 +77,23 @@ pub enum Mirroring {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NesRom {
|
impl NesRom {
|
||||||
pub fn load< T: Read + Seek >( fp: &mut T ) -> Result< Self, LoadError > {
|
pub fn load( mut data: &[u8] ) -> Result< Self, LoadError > {
|
||||||
let magic = try!( fp.read_u32::< LittleEndian >() );
|
if data.len() < 16 {
|
||||||
|
return Err( LoadError::new( "unexpected end of file" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
let magic = u32::from_le_bytes( [data[0], data[1], data[2], data[3]] );
|
||||||
if magic != 0x1a53454e {
|
if magic != 0x1a53454e {
|
||||||
return Err( LoadError::new( format!( "Not an INES ROM file: magic number mismatch (got: 0x{:08X})", magic ) ) );
|
return Err( LoadError::new( format!( "Not an INES ROM file: magic number mismatch (got: 0x{:08X})", magic ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
let rom_bank_count = try!( fp.read_u8() ) as usize;
|
let rom_bank_count = data[ 4 ] as usize;
|
||||||
let video_rom_bank_count = try!( fp.read_u8() ) as usize;
|
let video_rom_bank_count = data[ 5 ] as usize;
|
||||||
let flags_1 = try!( fp.read_u8() );
|
let flags_1 = data[ 6 ];
|
||||||
let flags_2 = try!( fp.read_u8() );
|
let flags_2 = data[ 7 ];
|
||||||
|
|
||||||
// For compatibility with older INES files we assume there must be always one RAM bank.
|
// For compatibility with older INES files we assume there must be always one RAM bank.
|
||||||
let save_ram_length = max( 1, try!( fp.read_u8() ) as u32 ) * 8 * 1024;
|
let save_ram_length = max( 1, data[ 8 ] as u32 ) * 8 * 1024;
|
||||||
|
|
||||||
// Skip padding.
|
|
||||||
try!( fp.seek( SeekFrom::Current(7) ) );
|
|
||||||
|
|
||||||
let mirroring = {
|
let mirroring = {
|
||||||
if flags_1 & 0b1000 != 0 {
|
if flags_1 & 0b1000 != 0 {
|
||||||
|
@ -131,20 +108,23 @@ impl NesRom {
|
||||||
let has_trainer = flags_1 & 0b100 != 0;
|
let has_trainer = flags_1 & 0b100 != 0;
|
||||||
let mapper = (flags_2 & 0xF0) | ((flags_1 & 0xF0) >> 4);
|
let mapper = (flags_2 & 0xF0) | ((flags_1 & 0xF0) >> 4);
|
||||||
|
|
||||||
let mut rom = Vec::< u8 >::with_capacity( rom_bank_count * ROM_BANK_SIZE );
|
let rom_size = rom_bank_count * ROM_BANK_SIZE;
|
||||||
let mut video_rom = Vec::< u8 >::with_capacity( video_rom_bank_count * VROM_BANK_SIZE );
|
let video_rom_size = video_rom_bank_count * VROM_BANK_SIZE;
|
||||||
|
|
||||||
unsafe {
|
|
||||||
rom.set_len( rom_bank_count * ROM_BANK_SIZE );
|
|
||||||
video_rom.set_len( video_rom_bank_count * VROM_BANK_SIZE );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
data = &data[ 16.. ];
|
||||||
if has_trainer {
|
if has_trainer {
|
||||||
try!( fp.seek( SeekFrom::Current( 512 ) ) ); // Skip trainer.
|
if data.len() < 512 {
|
||||||
|
return Err( LoadError::new( "unexpected end of file" ) );
|
||||||
|
}
|
||||||
|
data = &data[ 512.. ]; // Skip trainer.
|
||||||
}
|
}
|
||||||
|
|
||||||
try!( fp.read_exact( &mut rom[..] ) );
|
if data.len() < rom_size + video_rom_size {
|
||||||
try!( fp.read_exact( &mut video_rom[..] ) );
|
return Err( LoadError::new( "unexpected end of file" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
let rom = data[ ..rom_size ].to_vec();
|
||||||
|
let video_rom = data[ rom_size..rom_size + video_rom_size ].to_vec();
|
||||||
|
|
||||||
Ok( NesRom {
|
Ok( NesRom {
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::mem;
|
use core::mem;
|
||||||
use std::default::Default;
|
use core::default::Default;
|
||||||
use std::slice;
|
use core::slice;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use emumisc::{WrappingExtra, BitExtra, HiLoAccess, PeekPoke, At, is_b5_set, is_b6_set, is_b7_set, reverse_bits};
|
use emumisc::{WrappingExtra, BitExtra, HiLoAccess, PeekPoke, At, is_b5_set, is_b6_set, is_b7_set, reverse_bits};
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl nes_testsuite::EmulatorInterface for Instance {
|
||||||
testcase_state: testcase_state
|
testcase_state: testcase_state
|
||||||
};
|
};
|
||||||
|
|
||||||
try!( nes::Interface::load_rom_from_memory( &mut instance, rom_data ) );
|
try!( nes::Interface::load_rom( &mut instance, rom_data ) );
|
||||||
Ok( instance )
|
Ok( instance )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use std::path::Path;
|
use core::error::Error;
|
||||||
use std::error::Error;
|
use core::mem;
|
||||||
use std::fs::File;
|
use alloc::boxed::Box;
|
||||||
use std::io::{Read, Seek};
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use mos6502;
|
use mos6502;
|
||||||
use rp2c02;
|
use rp2c02;
|
||||||
|
@ -24,16 +22,8 @@ pub trait Context: Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Interface: Sized + Context {
|
pub trait Interface: Sized + Context {
|
||||||
fn load_rom< T: Read + Seek >( &mut self, stream: &mut T ) -> Result< (), LoadError > {
|
fn load_rom( &mut self, buffer: &[u8] ) -> Result< (), LoadError > {
|
||||||
Private::load_rom( self, stream )
|
Private::load_rom( self, buffer )
|
||||||
}
|
|
||||||
|
|
||||||
fn load_rom_from_memory( &mut self, buffer: &[u8] ) -> Result< (), LoadError > {
|
|
||||||
Private::load_rom_from_memory( self, buffer )
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_rom_from_file< P: AsRef< Path >>( &mut self, path: P ) -> Result< (), LoadError > {
|
|
||||||
Private::load_rom_from_file( self, path )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hard_reset( &mut self ) {
|
fn hard_reset( &mut self ) {
|
||||||
|
@ -319,8 +309,10 @@ trait Private: Sized + Context {
|
||||||
Orphan::< Self >::cast_mut( self )
|
Orphan::< Self >::cast_mut( self )
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rom< T: Read + Seek >( &mut self, stream: &mut T ) -> Result< (), LoadError > {
|
fn load_rom( &mut self, buffer: &[u8] ) -> Result< (), LoadError > {
|
||||||
let rom = try!( NesRom::load( stream ) );
|
let rom = try!( NesRom::load( buffer ) );
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
info!( "Loaded ROM: {:?}", rom );
|
info!( "Loaded ROM: {:?}", rom );
|
||||||
|
|
||||||
let mapper = try!( create_mapper( rom ) );
|
let mapper = try!( create_mapper( rom ) );
|
||||||
|
@ -332,17 +324,6 @@ trait Private: Sized + Context {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rom_from_memory( &mut self, buffer: &[u8] ) -> Result< (), LoadError > {
|
|
||||||
use std::io::Cursor;
|
|
||||||
let mut cursor = Cursor::new( buffer );
|
|
||||||
self.load_rom( &mut cursor )
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_rom_from_file< P: AsRef< Path >>( &mut self, path: P ) -> Result< (), LoadError > {
|
|
||||||
let mut fp = try!( File::open( path ) );
|
|
||||||
self.load_rom( &mut fp )
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hard_reset( &mut self ) {
|
fn hard_reset( &mut self ) {
|
||||||
let mut mapper: Box< Mapper + 'static > = Box::new( MapperNull );
|
let mut mapper: Box< Mapper + 'static > = Box::new( MapperNull );
|
||||||
mem::swap( &mut mapper, &mut self.state_mut().mapper );
|
mem::swap( &mut mapper, &mut self.state_mut().mapper );
|
||||||
|
@ -456,6 +437,7 @@ trait Private: Sized + Context {
|
||||||
4 => rp2c02::Interface::peek_oamdata( self.newtype_mut() ),
|
4 => rp2c02::Interface::peek_oamdata( self.newtype_mut() ),
|
||||||
7 => rp2c02::Interface::peek_ppudata( self.newtype_mut() ),
|
7 => rp2c02::Interface::peek_ppudata( self.newtype_mut() ),
|
||||||
_ => {
|
_ => {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
warn!( "Unhandled read from PPU register 0x{:04X}", address );
|
warn!( "Unhandled read from PPU register 0x{:04X}", address );
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
@ -495,7 +477,10 @@ trait Private: Sized + Context {
|
||||||
5 => rp2c02::Interface::poke_ppuscroll( self.newtype_mut(), value ),
|
5 => rp2c02::Interface::poke_ppuscroll( self.newtype_mut(), value ),
|
||||||
6 => rp2c02::Interface::poke_ppuaddr( self.newtype_mut(), value ),
|
6 => rp2c02::Interface::poke_ppuaddr( self.newtype_mut(), value ),
|
||||||
7 => rp2c02::Interface::poke_ppudata( self.newtype_mut(), value ),
|
7 => rp2c02::Interface::poke_ppudata( self.newtype_mut(), value ),
|
||||||
_ => warn!( "Unhandled write to PPU register 0x{:04X} (value=0x{:02X})", address, value )
|
_ => {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
warn!( "Unhandled write to PPU register 0x{:04X} (value=0x{:02X})", address, value )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
match translate_address_ioreg_other( address ) {
|
match translate_address_ioreg_other( address ) {
|
||||||
|
@ -535,7 +520,10 @@ trait Private: Sized + Context {
|
||||||
let is_on_odd_cycle = self.is_on_odd_cycle();
|
let is_on_odd_cycle = self.is_on_odd_cycle();
|
||||||
virtual_apu::Interface::poke_frame_sequencer_ctrl( self.newtype_mut(), value, is_on_odd_cycle )
|
virtual_apu::Interface::poke_frame_sequencer_ctrl( self.newtype_mut(), value, is_on_odd_cycle )
|
||||||
},
|
},
|
||||||
_ => warn!( "Unhandled write to IO register 0x{:04X} (value=0x{:02X})", address, value )
|
_ => {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
warn!( "Unhandled write to IO register 0x{:04X} (value=0x{:02X})", address, value )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
self.state().mapper.poke_expansion_rom( address, value );
|
self.state().mapper.poke_expansion_rom( address, value );
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
extern crate std as core;
|
||||||
|
|
||||||
extern crate sdl2;
|
extern crate sdl2;
|
||||||
extern crate clock_ticks;
|
extern crate clock_ticks;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
|
@ -170,7 +170,7 @@ impl UserInterface {
|
||||||
state: nes::State::new(),
|
state: nes::State::new(),
|
||||||
audio_buffer: Vec::new()
|
audio_buffer: Vec::new()
|
||||||
};
|
};
|
||||||
self.nes.load_rom_from_file( &self.rom_filename ).unwrap();
|
self.nes.load_rom( &std::fs::read( &self.rom_filename ).unwrap() ).unwrap();
|
||||||
|
|
||||||
let frame;
|
let frame;
|
||||||
loop {
|
loop {
|
||||||
|
@ -269,7 +269,8 @@ impl UserInterface {
|
||||||
if let Some( filename ) = env::args().skip(1).next() {
|
if let Some( filename ) = env::args().skip(1).next() {
|
||||||
println!( "Loading '{}'...", filename );
|
println!( "Loading '{}'...", filename );
|
||||||
self.rom_filename = PathBuf::from( &filename );
|
self.rom_filename = PathBuf::from( &filename );
|
||||||
self.nes.load_rom_from_file( filename ).unwrap();
|
let data = std::fs::read( filename ).unwrap();
|
||||||
|
self.nes.load_rom( &data ).unwrap();
|
||||||
|
|
||||||
self.is_emulating = true;
|
self.is_emulating = true;
|
||||||
self.image_buffer.clear();
|
self.image_buffer.clear();
|
||||||
|
|
|
@ -83,9 +83,15 @@ impl libretro_backend::Core for PinkyCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if let Some( data ) = game_data.data() {
|
let result = if let Some( data ) = game_data.data() {
|
||||||
nes::Interface::load_rom_from_memory( self, data )
|
nes::Interface::load_rom( self, data )
|
||||||
} else if let Some( path ) = game_data.path() {
|
} else if let Some( path ) = game_data.path() {
|
||||||
nes::Interface::load_rom_from_file( self, path )
|
let data = match std::fs::read( path ) {
|
||||||
|
Ok( data ) => data,
|
||||||
|
Err( _ ) => {
|
||||||
|
return LoadGameResult::Failed( game_data );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
nes::Interface::load_rom( self, &data )
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
|
|
@ -613,7 +613,7 @@ fn load_rom( pinky: &Rc< RefCell< PinkyWeb > >, rom_data: &[u8] ) {
|
||||||
|
|
||||||
let mut pinky = pinky.borrow_mut();
|
let mut pinky = pinky.borrow_mut();
|
||||||
let pinky = pinky.deref_mut();
|
let pinky = pinky.deref_mut();
|
||||||
if let Err( err ) = nes::Interface::load_rom_from_memory( pinky, rom_data ) {
|
if let Err( err ) = nes::Interface::load_rom( pinky, rom_data ) {
|
||||||
handle_error( err );
|
handle_error( err );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue