Add workspace and #[no_std] support

This commit is contained in:
Jan Bujak 2023-03-11 16:21:05 +00:00
parent 17c51a1e96
commit 59c2a652db
24 changed files with 153 additions and 142 deletions

18
.gitignore vendored
View file

@ -1,16 +1,2 @@
pinky-devui/target
pinky-devui/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
Cargo.toml
Cargo.lock

14
Cargo.toml Normal file
View 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

View file

@ -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_b1_set( value: u8 ) -> bool { (value & (1 << 1)) != 0 }
@ -11,8 +11,6 @@ use std::mem;
#[inline(always)]
pub fn to_bit( bit: u8, value: bool ) -> u8 {
use std::mem;
debug_assert!( bit < 8 );
let converted: u8 = unsafe { mem::transmute( value ) };
debug_assert!( converted == 0 || converted == 1 );
@ -37,7 +35,7 @@ macro_rules! impl_bit_extra {
impl BitExtra for $typ {
#[inline(always)]
fn mask_to_shift( mask: Self ) -> u8 {
use std::mem::size_of;
use core::mem::size_of;
debug_assert!( mask != 0 );
for n_bit in 0..(size_of::< Self >() * 8) as u8 {

View file

@ -1,4 +1,7 @@
#![no_std]
extern crate unreachable;
extern crate alloc;
#[macro_use]
mod misc;
@ -31,7 +34,7 @@ pub use memory::{as_bytes, allocate_slice, copy_memory};
#[macro_export]
macro_rules! impl_deref {
($container: ty, $field_type: ty, $field: ident) => (
impl ::std::ops::Deref for $container {
impl ::core::ops::Deref for $container {
type Target = $field_type;
#[inline(always)]
@ -40,7 +43,7 @@ macro_rules! impl_deref {
}
}
impl ::std::ops::DerefMut for $container {
impl ::core::ops::DerefMut for $container {
#[inline(always)]
fn deref_mut( &mut self ) -> &mut Self::Target {
&mut self.$field
@ -52,14 +55,14 @@ macro_rules! impl_deref {
#[macro_export]
macro_rules! impl_as_ref {
($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)]
fn as_ref( &self ) -> &$field_type {
&self.$field
}
}
impl ::std::convert::AsMut< $field_type > for $container {
impl ::core::convert::AsMut< $field_type > for $container {
#[inline(always)]
fn as_mut( &mut self ) -> &mut $field_type {
&mut self.$field
@ -74,7 +77,7 @@ macro_rules! newtype {
$(#[$attr])*
pub struct $new( pub $old );
impl ::std::ops::Deref for $new {
impl ::core::ops::Deref for $new {
type Target = $old;
#[inline(always)]
@ -83,21 +86,21 @@ macro_rules! newtype {
}
}
impl ::std::ops::DerefMut for $new {
impl ::core::ops::DerefMut for $new {
#[inline(always)]
fn deref_mut( &mut self ) -> &mut Self::Target {
&mut self.0
}
}
impl ::std::convert::AsRef< $old > for $new {
impl ::core::convert::AsRef< $old > for $new {
#[inline(always)]
fn as_ref( &self ) -> &$old {
&self.0
}
}
impl ::std::convert::AsMut< $old > for $new {
impl ::core::convert::AsMut< $old > for $new {
#[inline(always)]
fn as_mut( &mut self ) -> &mut $old {
&mut self.0

View file

@ -1,6 +1,8 @@
use std::ptr;
use std::slice;
use std::mem;
use core::ptr;
use core::slice;
use core::mem;
use alloc::boxed::Box;
use alloc::vec::Vec;
#[inline]
pub fn as_bytes< T: Copy >( array: &[T] ) -> &[u8] {

View file

@ -9,3 +9,6 @@ bitflags = "0.7"
[dependencies.emumisc]
path = "../emumisc"
[features]
default = ["std"]
std = []

View file

@ -345,9 +345,8 @@ def generate_opcode_attributes
#[inline(always)]
fn get_register( &self ) -> Register8 {
use std::mem;
unsafe {
mem::transmute( (self.attr & 0b00011000) >> 3 )
core::mem::transmute( (self.attr & 0b00011000) >> 3 )
}
}

View file

@ -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_upper_case_globals)]
#[cfg(feature = "std")]
extern crate std as core;
#[macro_use]
extern crate emumisc;

View file

@ -1,10 +1,9 @@
use self::Register8::*;
use self::Direction::*;
use std::mem::transmute;
use std::fmt;
use std::ops::Deref;
use std::error;
use core::mem::transmute;
use core::fmt;
use core::ops::Deref;
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 {
use self::EmulationError::*;
match *self {
@ -1018,6 +1017,7 @@ trait Private: Sized + Context {
result
}
#[cfg(feature = "std")]
fn print_registers( &self ) {
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 );

View file

@ -761,9 +761,8 @@ macro_rules! decoding_logic {
#[inline(always)]
fn get_register( &self ) -> Register8 {
use std::mem;
unsafe {
mem::transmute( (self.attr & 0b00011000) >> 3 )
core::mem::transmute( (self.attr & 0b00011000) >> 3 )
}
}

View file

@ -4,8 +4,7 @@ version = "0.1.0"
authors = ["Jan Bujak <j@exia.io>"]
[dependencies]
byteorder = "0.5"
log = "0.3"
log = { version = "0.4", default-features = false, optional = true }
bitflags = "0.7"
[dependencies.emumisc]
@ -13,6 +12,7 @@ path = "../emumisc"
[dependencies.mos6502]
path = "../mos6502"
default-features = false
[dev-dependencies.nes-testsuite]
path = "../nes-testsuite"
@ -20,10 +20,6 @@ path = "../nes-testsuite"
[dev-dependencies.rp2c02-testsuite]
path = "../rp2c02-testsuite"
[profile.test]
opt-level = 2
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 4
[features]
default = ["std"]
std = ["mos6502/std", "log", "log/std"]

View file

@ -1,4 +1,6 @@
use std::fmt;
use core::fmt;
use alloc::vec::Vec;
use emumisc::{PeekPoke, At};
use rom::Mirroring;
use mappers::Mapper;
@ -464,6 +466,7 @@ impl GenericMapper {
#[inline]
pub fn poke_cpu_memory_space( &mut self, address: u16, value: u8 ) {
if self.is_cpu_address_writable( address ) == false {
#[cfg(feature = "log")]
warn!( "Unhandled write to 0x{:04X} (value=0x{:02X})", address, value );
return;
}
@ -523,7 +526,7 @@ impl Mapper for GenericMapper {
}
}
use std::ops::Sub;
use core::ops::Sub;
use rom::{NesRom, LoadError};
#[inline]
fn wraparound< T: Sub< Output = T > + PartialOrd + Copy >( limit: T, mut value: T ) -> T {

View file

@ -1,11 +1,22 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(error_in_core))]
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
#![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]
extern crate log;

View file

@ -152,6 +152,7 @@ impl Mapper for MapperMMC1 {
_ => unsafe { fast_unreachable!() }
};
#[cfg(feature = "log")]
debug!( "ROM switching mode = {:?}, VROM switching mode = {:?}",
self.rom_switching_mode,
self.vrom_switching_mode

View file

@ -1,3 +1,6 @@
use alloc::boxed::Box;
use alloc::format;
use rom::{NesRom, LoadError};
use generic_mapper::GenericMapper;
use mapper_mmc1::MapperMMC1;
@ -10,20 +13,24 @@ pub trait Mapper {
fn poke_rom( &mut self, address: u16, value: u8 );
fn peek_sram( &self, address: u16 ) -> u8 {
#[cfg(feature = "log")]
warn!( "Unhandled read from the save RAM at 0x{:04X}", address );
0
}
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 );
}
fn peek_expansion_rom( &self, address: u16 ) -> u8 {
#[cfg(feature = "log")]
warn!( "Unhandled read from the expansion ROM at 0x{:04X}", address );
0
}
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 );
}
@ -35,20 +42,24 @@ pub struct MapperNull;
impl Mapper for MapperNull {
fn peek_rom( &self, address: u16 ) -> u8 {
#[cfg(feature = "log")]
warn!( "Unhandled read from the ROM at 0x{:04X}", address );
0
}
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 );
}
fn peek_video_memory( &self, address: u16 ) -> u8 {
#[cfg(feature = "log")]
warn!( "Unhandled read from the VROM at 0x{:04X}", address );
0
}
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 );
}
}
@ -65,6 +76,7 @@ pub fn create_mapper( rom: NesRom ) -> Result< Box< Mapper >, LoadError > {
mapper.initialize_video_rom( &rom.video_rom[..] );
mapper.initialize_background_tilemaps( rom.mirroring );
#[cfg(feature = "log")]
debug!( "Initialized mapper: {:?}", mapper );
Ok( Box::new( mapper ) )
},

View file

@ -1,5 +1,5 @@
use std::mem;
use std::ops::{Deref, DerefMut};
use core::mem;
use core::ops::{Deref, DerefMut};
// 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.

View file

@ -1,23 +1,9 @@
use std::io;
use std::io::{Read, Error, ErrorKind, Seek, SeekFrom};
use std::cmp::max;
use std::fmt;
use std::error;
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 )
}
}
use core::cmp::max;
use core::fmt;
use core::error;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::format;
fn decapitalize< M: fmt::Display >( msg: M ) -> String {
let message = format!( "{}", msg );
@ -32,7 +18,6 @@ fn decapitalize< M: fmt::Display >( msg: M ) -> String {
#[derive(Debug)]
pub enum LoadError {
Custom( String ),
IO( io::Error )
}
impl LoadError {
@ -45,7 +30,6 @@ impl fmt::Display for LoadError {
fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
match *self {
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(())
@ -56,17 +40,10 @@ impl error::Error for LoadError {
fn description( &self ) -> &str {
match *self {
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 VROM_BANK_SIZE: usize = 8 * 1024;
@ -100,23 +77,23 @@ pub enum Mirroring {
}
impl NesRom {
pub fn load< T: Read + Seek >( fp: &mut T ) -> Result< Self, LoadError > {
let magic = try!( fp.read_u32::< LittleEndian >() );
pub fn load( mut data: &[u8] ) -> Result< Self, LoadError > {
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 {
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 video_rom_bank_count = try!( fp.read_u8() ) as usize;
let flags_1 = try!( fp.read_u8() );
let flags_2 = try!( fp.read_u8() );
let rom_bank_count = data[ 4 ] as usize;
let video_rom_bank_count = data[ 5 ] as usize;
let flags_1 = data[ 6 ];
let flags_2 = data[ 7 ];
// 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;
// Skip padding.
try!( fp.seek( SeekFrom::Current(7) ) );
let save_ram_length = max( 1, data[ 8 ] as u32 ) * 8 * 1024;
let mirroring = {
if flags_1 & 0b1000 != 0 {
@ -131,20 +108,23 @@ impl NesRom {
let has_trainer = flags_1 & 0b100 != 0;
let mapper = (flags_2 & 0xF0) | ((flags_1 & 0xF0) >> 4);
let mut rom = Vec::< u8 >::with_capacity( rom_bank_count * ROM_BANK_SIZE );
let mut video_rom = Vec::< u8 >::with_capacity( 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 );
}
let rom_size = rom_bank_count * ROM_BANK_SIZE;
let video_rom_size = video_rom_bank_count * VROM_BANK_SIZE;
data = &data[ 16.. ];
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[..] ) );
try!( fp.read_exact( &mut video_rom[..] ) );
if data.len() < rom_size + video_rom_size {
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 {
mapper: mapper,

View file

@ -1,6 +1,7 @@
use std::mem;
use std::default::Default;
use std::slice;
use core::mem;
use core::default::Default;
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};

View file

@ -39,7 +39,7 @@ impl nes_testsuite::EmulatorInterface for Instance {
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 )
}

View file

@ -1,8 +1,6 @@
use std::path::Path;
use std::error::Error;
use std::fs::File;
use std::io::{Read, Seek};
use std::mem;
use core::error::Error;
use core::mem;
use alloc::boxed::Box;
use mos6502;
use rp2c02;
@ -24,16 +22,8 @@ pub trait Context: Sized {
}
pub trait Interface: Sized + Context {
fn load_rom< T: Read + Seek >( &mut self, stream: &mut T ) -> Result< (), LoadError > {
Private::load_rom( self, stream )
}
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 load_rom( &mut self, buffer: &[u8] ) -> Result< (), LoadError > {
Private::load_rom( self, buffer )
}
fn hard_reset( &mut self ) {
@ -319,8 +309,10 @@ trait Private: Sized + Context {
Orphan::< Self >::cast_mut( self )
}
fn load_rom< T: Read + Seek >( &mut self, stream: &mut T ) -> Result< (), LoadError > {
let rom = try!( NesRom::load( stream ) );
fn load_rom( &mut self, buffer: &[u8] ) -> Result< (), LoadError > {
let rom = try!( NesRom::load( buffer ) );
#[cfg(feature = "log")]
info!( "Loaded ROM: {:?}", rom );
let mapper = try!( create_mapper( rom ) );
@ -332,17 +324,6 @@ trait Private: Sized + Context {
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 ) {
let mut mapper: Box< Mapper + 'static > = Box::new( MapperNull );
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() ),
7 => rp2c02::Interface::peek_ppudata( self.newtype_mut() ),
_ => {
#[cfg(feature = "log")]
warn!( "Unhandled read from PPU register 0x{:04X}", address );
0
}
@ -495,7 +477,10 @@ trait Private: Sized + Context {
5 => rp2c02::Interface::poke_ppuscroll( self.newtype_mut(), value ),
6 => rp2c02::Interface::poke_ppuaddr( 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 ) {
@ -535,7 +520,10 @@ trait Private: Sized + Context {
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 )
},
_ => 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 );

View file

@ -1,3 +1,5 @@
extern crate std as core;
extern crate sdl2;
extern crate clock_ticks;
extern crate serde_json;

View file

@ -170,7 +170,7 @@ impl UserInterface {
state: nes::State::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;
loop {
@ -269,7 +269,8 @@ impl UserInterface {
if let Some( filename ) = env::args().skip(1).next() {
println!( "Loading '{}'...", 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.image_buffer.clear();

View file

@ -83,9 +83,15 @@ impl libretro_backend::Core for PinkyCore {
}
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() {
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 {
unreachable!();
};

View file

@ -613,7 +613,7 @@ fn load_rom( pinky: &Rc< RefCell< PinkyWeb > >, rom_data: &[u8] ) {
let mut pinky = pinky.borrow_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 );
return;
}