├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── src ├── lib.rs ├── box_array.rs ├── debugger │ └── mod.rs ├── cdrom │ ├── simple_rand.rs │ ├── iso9660.rs │ └── disc.rs ├── shared.rs ├── interrupt.rs ├── parallel_io │ ├── mod.rs │ └── exe_loader.rs ├── debug_uart.rs ├── gpu │ └── renderer.rs ├── cpu │ ├── gte │ │ └── divider.rs │ ├── cop0.rs │ └── tests.rs ├── tracer.rs ├── bios │ ├── mod.rs │ └── db.rs ├── serializer.rs ├── memory │ ├── ram.rs │ ├── dma.rs │ └── timers.rs ├── padmemcard │ ├── gamepad.rs │ └── mod.rs ├── timekeeper.rs ├── mdec │ └── mod.rs └── spu │ └── mod.rs ├── Cargo.toml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /target 3 | /roms 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cdimage"] 2 | path = cdimage 3 | url = https://github.com/simias/cdimage.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: 3 | - linux 4 | before_script: 5 | - rustc -V 6 | - cargo -V 7 | script: 8 | - cargo build -v 9 | - cargo build -v --features trace 10 | - cargo test 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Rustation - a PlayStation emulator 2 | Copyright (C) 2015 Lionel Flandrin 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU General Public License 6 | as published by the Free Software Foundation; either version 2 7 | of the License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate shaman; 4 | extern crate cdimage; 5 | extern crate arrayvec; 6 | extern crate rustc_serialize; 7 | 8 | #[cfg(feature = "trace")] 9 | #[macro_use] 10 | extern crate lazy_static; 11 | 12 | #[macro_use] 13 | mod box_array; 14 | #[macro_use] 15 | mod serializer; 16 | #[macro_use] 17 | pub mod tracer; 18 | 19 | pub mod gpu; 20 | pub mod cdrom; 21 | pub mod bios; 22 | pub mod memory; 23 | pub mod cpu; 24 | pub mod shared; 25 | pub mod padmemcard; 26 | pub mod debugger; 27 | pub mod assembler; 28 | pub mod parallel_io; 29 | pub mod debug_uart; 30 | 31 | mod interrupt; 32 | mod timekeeper; 33 | mod spu; 34 | mod mdec; 35 | 36 | mod version { 37 | // VERSION and VERSION_CSTR are generated by build.rs 38 | include!(concat!(env!("OUT_DIR"), "/version.rs")); 39 | } 40 | 41 | pub use version::VERSION; 42 | pub use version::VERSION_CSTR; 43 | -------------------------------------------------------------------------------- /src/box_array.rs: -------------------------------------------------------------------------------- 1 | //! Allocating fixed size arrays on the heap without unsafe code is 2 | //! pretty hard while the box syntax is unstable. As a workaround we 3 | //! implement a macro which does just that. 4 | 5 | /// A macro similar to `vec![$elem; $size]` which returns a boxed 6 | /// array. 7 | /// 8 | /// ```rustc 9 | /// let _: Box<[u8; 1024]> = box_array![0; 1024]; 10 | /// ``` 11 | macro_rules! box_array { 12 | ($val:expr ; $len:expr) => ({ 13 | // Use a generic function so that the pointer cast remains 14 | // type-safe 15 | fn vec_to_boxed_array(vec: Vec) -> Box<[T; $len]> { 16 | let boxed_slice = vec.into_boxed_slice(); 17 | 18 | let ptr = 19 | ::std::boxed::Box::into_raw(boxed_slice) as *mut [T; $len]; 20 | 21 | unsafe { Box::from_raw(ptr) } 22 | } 23 | 24 | vec_to_boxed_array(vec![$val; $len]) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "rustation" 4 | 5 | # Should match the latest git tag! This version info is used to set 6 | # the internal `VERSION` string if "git describe" is unavailable. 7 | version = "0.0.3" 8 | 9 | authors = ["Lionel Flandrin "] 10 | description = "A PlayStation emulator" 11 | homepage = "https://github.com/simias/rustation" 12 | repository = "https://github.com/simias/rustation" 13 | documentation = "https://github.com/simias/rustation/wiki" 14 | readme = "README.md" 15 | license = "GPL-2.0+" 16 | keywords = ["emulator", "playstation"] 17 | 18 | build = "build.rs" 19 | 20 | [features] 21 | trace = [ "lazy_static" ] 22 | 23 | [dependencies] 24 | shaman = "0.1" 25 | log = "0.3" 26 | arrayvec = "0.4" 27 | rustc-serialize = "0.3" 28 | lazy_static = { version = "0.2", optional = true } 29 | 30 | [lib] 31 | name = "rustation" 32 | crate-type = ["rlib"] 33 | 34 | [dependencies.cdimage] 35 | path = "cdimage" 36 | -------------------------------------------------------------------------------- /src/debugger/mod.rs: -------------------------------------------------------------------------------- 1 | use cpu::Cpu; 2 | 3 | /// Trait defining the debugger interface 4 | pub trait Debugger { 5 | /// Signal a "break" which will put the emulator in debug mode at 6 | /// the next instruction 7 | fn trigger_break(&mut self); 8 | 9 | /// Called by the CPU when it's about to execute a new 10 | /// instruction. This function is called before *all* CPU 11 | /// instructions so it needs to be as fast as possible. 12 | fn pc_change(&mut self, cpu: &mut Cpu); 13 | 14 | /// Called by the CPU when it's about to load a value from memory. 15 | fn memory_read(&mut self, cpu: &mut Cpu, addr: u32); 16 | 17 | /// Called by the CPU when it's about to write a value to memory. 18 | fn memory_write(&mut self, cpu: &mut Cpu, addr: u32); 19 | } 20 | 21 | 22 | /// Dummy debugger implementation that does nothing. Can be used when 23 | /// debugging is disabled. 24 | impl Debugger for () { 25 | fn trigger_break(&mut self) { 26 | } 27 | 28 | fn pc_change(&mut self, _: &mut Cpu) { 29 | } 30 | 31 | fn memory_read(&mut self, _: &mut Cpu, _: u32) { 32 | } 33 | 34 | fn memory_write(&mut self, _: &mut Cpu, _: u32) { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/cdrom/simple_rand.rs: -------------------------------------------------------------------------------- 1 | /// Fast non-cryptographically secure RNG. The implementation is 2 | /// XorShift with a period of (2**31)-1. This is more than sufficient 3 | /// for our use case. This RNG is of course fully deterministic and 4 | /// will always return the same sequence since the seed is fixed. 5 | /// 6 | /// See http://www.jstatsoft.org/v08/i14/paper for more details on the 7 | /// algorithm. 8 | /// 9 | /// One of the pitfalls of this algorithm is that if the output is 10 | /// used as a raw 32bit random number (without modulo) it'll never 11 | /// return 0. 12 | #[derive(RustcDecodable, RustcEncodable)] 13 | pub struct SimpleRand { 14 | state: u32, 15 | } 16 | 17 | impl SimpleRand { 18 | 19 | /// Create a new FastRand instance using a hardcoded seed 20 | pub fn new() -> SimpleRand { 21 | SimpleRand { 22 | // Arbitrary seed, must be non-0 23 | state: 1, 24 | } 25 | } 26 | 27 | /// Run through one cycle of XorShift and return the internal 28 | /// pseudo-random state. It will *never* return 0. 29 | pub fn next(&mut self) -> u32 { 30 | // The XorShift paper lists a bunch of valid shift triplets, I 31 | // picked one at random. 32 | self.state ^= self.state << 6; 33 | self.state ^= self.state >> 1; 34 | self.state ^= self.state << 11; 35 | 36 | self.state 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/shared.rs: -------------------------------------------------------------------------------- 1 | use timekeeper::TimeKeeper; 2 | use interrupt::InterruptState; 3 | 4 | /// State shared between various modules 5 | #[derive(RustcDecodable, RustcEncodable)] 6 | pub struct SharedState { 7 | tk: TimeKeeper, 8 | irq_state: InterruptState, 9 | counters: Counters, 10 | } 11 | 12 | impl SharedState { 13 | pub fn new() -> SharedState { 14 | SharedState { 15 | tk: TimeKeeper::new(), 16 | irq_state: InterruptState::new(), 17 | counters: Counters::new(), 18 | } 19 | } 20 | 21 | pub fn tk(&mut self) -> &mut TimeKeeper { 22 | &mut self.tk 23 | } 24 | 25 | pub fn irq_state(&mut self) -> &InterruptState { 26 | &self.irq_state 27 | } 28 | 29 | pub fn irq_state_mut(&mut self) -> &mut InterruptState { 30 | &mut self.irq_state 31 | } 32 | 33 | pub fn counters(&self) -> &Counters { 34 | &self.counters 35 | } 36 | 37 | pub fn counters_mut(&mut self) -> &mut Counters { 38 | &mut self.counters 39 | } 40 | } 41 | 42 | /// Struct holding various counters for debugging and profiling 43 | #[derive(RustcDecodable, RustcEncodable)] 44 | pub struct Counters { 45 | /// Increments at each frame drawn to the display. It will wrap in 46 | /// a little more than 2 years at 60Hz. 47 | pub frame: Counter, 48 | /// Increments when the game sets the display area coordinates, it 49 | /// usually means that the game wants to display a new frame and 50 | /// can be used to compute the internal FPS. 51 | pub framebuffer_swap: Counter, 52 | /// Incremented when the CPU is preempted by an external 53 | /// interrupt. 54 | pub cpu_interrupt: Counter, 55 | } 56 | 57 | impl Counters { 58 | pub fn new() -> Counters { 59 | Counters { 60 | frame: Counter(0), 61 | framebuffer_swap: Counter(0), 62 | cpu_interrupt: Counter(0), 63 | } 64 | } 65 | } 66 | 67 | /// Simple wrapper around a `u32` to implement a counter interface 68 | #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] 69 | pub struct Counter(u32); 70 | 71 | impl Counter { 72 | pub fn reset(&mut self) { 73 | self.0 = 0 74 | } 75 | 76 | pub fn increment(&mut self) { 77 | self.0 = self.0.wrapping_add(1) 78 | } 79 | 80 | pub fn get(&self) -> u32 { 81 | self.0 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/interrupt.rs: -------------------------------------------------------------------------------- 1 | /// The PlayStation supports 10 interrupts 2 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 3 | #[derive(RustcDecodable, RustcEncodable)] 4 | pub enum Interrupt { 5 | /// Display in vertical blanking 6 | VBlank = 0, 7 | /// CDROM controller 8 | CdRom = 2, 9 | /// DMA transfer done 10 | Dma = 3, 11 | /// Timer0 interrupt 12 | Timer0 = 4, 13 | /// Timer1 interrupt 14 | Timer1 = 5, 15 | /// Timer2 interrupt 16 | Timer2 = 6, 17 | /// Gamepad and Memory Card controller interrupt 18 | PadMemCard = 7, 19 | } 20 | 21 | #[derive(Clone, Copy, RustcDecodable, RustcEncodable)] 22 | pub struct InterruptState { 23 | /// Interrupt status 24 | status: u16, 25 | /// Interrupt mask 26 | mask: u16, 27 | } 28 | 29 | impl InterruptState { 30 | 31 | pub fn new() -> InterruptState { 32 | InterruptState { 33 | status: 0, 34 | mask: 0, 35 | } 36 | } 37 | 38 | /// Return true if at least one interrupt is asserted and not 39 | /// masked 40 | pub fn active(self) -> bool { 41 | (self.status & self.mask) != 0 42 | } 43 | 44 | pub fn status(self) -> u16 { 45 | self.status 46 | } 47 | 48 | /// Acknowledge interrupts by writing 0 to the corresponding bit 49 | pub fn ack(&mut self, ack: u16) { 50 | self.status &= ack; 51 | } 52 | 53 | pub fn mask(self) -> u16 { 54 | self.mask 55 | } 56 | 57 | pub fn set_mask(&mut self, mask: u16) { 58 | // Temporary hack: trigger an error if a non-implemented 59 | // interrupt is requested 60 | let supported = [ Interrupt::VBlank, 61 | Interrupt::CdRom, 62 | Interrupt::Dma, 63 | Interrupt::Timer0, 64 | Interrupt::Timer1, 65 | Interrupt::Timer2, 66 | Interrupt::PadMemCard]; 67 | 68 | let rem = supported.iter().fold(mask, 69 | |mask, &it| mask & !(1 << it as u16)); 70 | 71 | if rem != 0 { 72 | panic!("Unsupported interrupt: {:04x}", rem); 73 | } 74 | 75 | self.mask = mask; 76 | } 77 | 78 | /// Trigger the interrupt `which`, must be called on the rising 79 | /// edge of the interrupt signal. 80 | pub fn assert(&mut self, which: Interrupt) { 81 | self.status |= 1 << (which as usize); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/parallel_io/mod.rs: -------------------------------------------------------------------------------- 1 | //! Emulation of the parallel interface (the Parallel I/O connector on 2 | //! the back of older PlayStation models) 3 | 4 | use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; 5 | 6 | use memory::Addressable; 7 | use shared::SharedState; 8 | 9 | pub mod exe_loader; 10 | 11 | pub struct ParallelIo { 12 | module: Box, 13 | } 14 | 15 | impl ParallelIo { 16 | 17 | pub fn disconnected() -> ParallelIo { 18 | ParallelIo { 19 | module: Box::new(Disconnected), 20 | } 21 | } 22 | 23 | pub fn set_module(&mut self, module: Box) { 24 | self.module = module; 25 | } 26 | 27 | pub fn load(&mut self, 28 | shared: &mut SharedState, 29 | offset: u32) -> u32 { 30 | let mut r = 0; 31 | 32 | for i in 0..T::size() { 33 | let b = self.module.load(shared, offset + i as u32); 34 | 35 | r |= (b as u32) << (8 * i); 36 | } 37 | 38 | r 39 | } 40 | } 41 | 42 | impl Encodable for ParallelIo { 43 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 44 | // Since the parallel interface is supposed to support a whole 45 | // bunch of modules (some of which potentially defined outside 46 | // of this crate) it's not trivial to serialize them. For not 47 | // let's ignore it and not include it in the savestate. 48 | s.emit_nil() 49 | } 50 | } 51 | 52 | impl Decodable for ParallelIo { 53 | fn decode(d: &mut D) -> Result { 54 | try!(d.read_nil()); 55 | 56 | Ok(ParallelIo::disconnected()) 57 | } 58 | } 59 | 60 | /// Since there can be all sorts of hardware connected to the Parallel 61 | /// I/O port I abstract it behind a trait interface 62 | pub trait ParallelIoModule { 63 | /// Parallel I/O load 8bits at offset `offset` (within the 64 | /// expansion 1 memory region) 65 | fn load(&mut self, shared: &mut SharedState, offset: u32) -> u8; 66 | 67 | /// Parallel I/O byte store at offset `offset` (within the expansion 1 68 | /// memory region) 69 | fn store(&mut self, shared: &mut SharedState, offset: u32, val: u8); 70 | } 71 | 72 | /// A dummy implementation of ParallelIo when nothing is connected 73 | pub struct Disconnected; 74 | 75 | impl ParallelIoModule for Disconnected { 76 | fn load(&mut self, _: &mut SharedState, _: u32) -> u8 { 77 | // When no expansion is present the CPU reads full ones 78 | !0 79 | } 80 | 81 | fn store(&mut self, _: &mut SharedState, _: u32, _: u8) { 82 | // NOP 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/debug_uart.rs: -------------------------------------------------------------------------------- 1 | //! Emulation of the dual debugging UART on expansion 2, used to get 2 | //! debugging messages from various games and programs 3 | //! 4 | //! I only implemented the bare minimum to catch debug messages sent 5 | //! by some applications. 6 | 7 | use memory::Addressable; 8 | use shared::SharedState; 9 | 10 | #[derive(RustcDecodable, RustcEncodable)] 11 | pub struct DebugUart { 12 | /// We don't want to display the TX data one character at a time 13 | /// so we attempt to line buffer it. 14 | tx_buffers: [String; 2], 15 | } 16 | 17 | impl DebugUart { 18 | pub fn new() -> DebugUart { 19 | DebugUart { 20 | tx_buffers: [String::with_capacity(TX_BUFFER_LEN), 21 | String::with_capacity(TX_BUFFER_LEN)], 22 | } 23 | } 24 | 25 | pub fn load(&mut self, 26 | _: &mut SharedState, 27 | offset: u32) -> u32 { 28 | if A::size() != 1 { 29 | panic!("Unhandled debug UART load ({})", A::size()) 30 | } 31 | 32 | match offset { 33 | // UART status register A. Return "Tx ready" bit set. 34 | 0x21 => 1 << 2, 35 | _ => panic!("Unhandled debug UART store: {:x}", 36 | offset), 37 | } 38 | } 39 | 40 | pub fn store(&mut self, 41 | _: &mut SharedState, 42 | offset: u32, 43 | val: u32) { 44 | 45 | if A::size() != 1 { 46 | panic!("Unhandled debug UART store ({})", A::size()) 47 | } 48 | 49 | let val = val as u8; 50 | 51 | match offset { 52 | // UART mode register 2.A 53 | 0x20 => (), 54 | // Clock select register A 55 | 0x21 => (), 56 | // Command register A 57 | 0x22 => (), 58 | // UART Tx register A 59 | 0x23 => self.push_char(0, val as char), 60 | // Control register 61 | 0x24 => (), 62 | // Command register B 63 | 0x2a => (), 64 | // Output port configuration register 65 | 0x2d => (), 66 | // Set output port bits 67 | 0x2e => (), 68 | // Interrupt mask register 69 | 0x25 => { 70 | // We don't implement interrupts for now 71 | if val != 0 { 72 | panic!("Unhandled debug UART interrupt mask: {:02x}", 73 | val); 74 | } 75 | } 76 | // Boot status register, is incremented by the BIOS during 77 | // bootup 78 | 0x41 => debug!("BIOS boot status: {}", val), 79 | _ => panic!("Unhandled debug UART store: {:x} {:02x}", 80 | offset, val), 81 | } 82 | } 83 | 84 | fn push_char(&mut self, port: usize, c: char) { 85 | let buffer = &mut self.tx_buffers[port]; 86 | 87 | if c == '\n' || buffer.len() == buffer.capacity() { 88 | let uart = 89 | match port { 90 | 0 => 'A', 91 | 1 => 'B', 92 | _ => unreachable!(), 93 | }; 94 | 95 | debug!("Debug UART {}: {}", uart, buffer); 96 | buffer.clear(); 97 | } else { 98 | buffer.push(c); 99 | } 100 | } 101 | } 102 | 103 | /// Maximum length of the Tx buffer before displaying the message even 104 | /// if no newline is encountered 105 | const TX_BUFFER_LEN: usize = 1024; 106 | -------------------------------------------------------------------------------- /src/gpu/renderer.rs: -------------------------------------------------------------------------------- 1 | pub trait Renderer { 2 | fn set_draw_offset(&mut self, x: i16, y: i16); 3 | fn set_draw_area(&mut self, top_left: (u16, u16), dimensions: (u16, u16)); 4 | 5 | fn set_display_mode(&mut self, 6 | top_left: (u16, u16), 7 | resolution: (u16, u16), 8 | depth_24bpp: bool); 9 | 10 | fn push_line(&mut self, &PrimitiveAttributes, &[Vertex; 2]); 11 | fn push_triangle(&mut self, &PrimitiveAttributes, &[Vertex; 3]); 12 | fn push_quad(&mut self, &PrimitiveAttributes, &[Vertex; 4]); 13 | 14 | fn fill_rect(&mut self, 15 | color: [u8; 3], 16 | top_left: (u16, u16), 17 | dimensions: (u16, u16)); 18 | 19 | fn load_image(&mut self, 20 | top_left: (u16, u16), 21 | dimensions: (u16, u16), 22 | pixel_buffer: &[u16]); 23 | } 24 | 25 | pub struct Vertex { 26 | pub position: [i16; 2], 27 | pub color: [u8; 3], 28 | pub texture_coord: [u16; 2], 29 | } 30 | 31 | impl Vertex { 32 | pub fn new(position: [i16; 2], color: [u8; 3]) -> Vertex { 33 | Vertex { 34 | position: position, 35 | color: color, 36 | // Unused 37 | texture_coord: [0, 0], 38 | } 39 | } 40 | 41 | pub fn new_textured(position: [i16; 2], 42 | color: [u8; 3], 43 | texture_coord: [u16; 2]) -> Vertex { 44 | Vertex { 45 | position: position, 46 | color: color, 47 | texture_coord: texture_coord, 48 | } 49 | } 50 | } 51 | 52 | #[derive(RustcDecodable, RustcEncodable)] 53 | pub struct PrimitiveAttributes { 54 | /// If true then the equation defined by `semi_transparency_mode` 55 | /// is applied to semi-transparent pixels. 56 | pub semi_transparent: bool, 57 | /// When `semi_transparent` is true this defines the blending 58 | /// equation for semi-transparent pixels. 59 | pub semi_transparency_mode: SemiTransparencyMode, 60 | /// Blending equation, says if the primitive is simply gouraud 61 | /// shaded (or monochrome since it's just a special case of 62 | /// gouraud shading with the same color on all vertices), 63 | /// texture-mapped or a mix of both (texture blending). 64 | pub blend_mode: BlendMode, 65 | /// For textured primitives this contains the coordinates of the 66 | /// top-left coordinates of the texture page. Texture pages are 67 | /// always 256x256 pixels big and wrap around in case of 68 | /// out-of-bound access. 69 | pub texture_page: [u16; 2], 70 | /// The PlayStation GPU supports 4 and 8bpp paletted textures and 71 | /// 16bits "truecolor" textures. 72 | pub texture_depth: TextureDepth, 73 | /// For 4 and 8bpp paletted textures this contains the coordinates 74 | /// of the first entry of the palette. The next entries will be at 75 | /// x + 1, x + 2 etc... 76 | pub clut: [u16; 2], 77 | /// True if the primitive is dithered. 78 | pub dither: bool, 79 | } 80 | 81 | /// Primitive texturing methods 82 | #[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] 83 | pub enum BlendMode { 84 | /// No texture 85 | None, 86 | /// Raw texture 87 | Raw, 88 | /// Texture bledend with the monochrome/shading color 89 | Blended, 90 | } 91 | 92 | /// Semi-transparency modes supported by the PlayStation GPU 93 | #[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] 94 | pub enum SemiTransparencyMode { 95 | /// Source / 2 + destination / 2 96 | Average = 0, 97 | /// Source + destination 98 | Add = 1, 99 | /// Destination - source 100 | SubstractSource = 2, 101 | /// Destination + source / 4 102 | AddQuarterSource = 3, 103 | } 104 | 105 | /// Depth of the pixel values in a texture page 106 | #[derive(Clone, Copy, RustcDecodable, RustcEncodable)] 107 | pub enum TextureDepth { 108 | /// 4 bits per pixel, paletted 109 | T4Bpp = 0, 110 | /// 8 bits per pixel, paletted 111 | T8Bpp = 1, 112 | /// 16 bits per pixel, truecolor 113 | T16Bpp = 2, 114 | } 115 | -------------------------------------------------------------------------------- /src/cpu/gte/divider.rs: -------------------------------------------------------------------------------- 1 | /// GTE division algorithm: returns a saturated 1.16 value. Divisions 2 | /// by 0 shouldn't occur since we clip against the near plane but they 3 | /// would saturate to 0x1ffff anyway. 4 | /// 5 | /// The algorithm is based on Newton–Raphson, I copied mednafen's 6 | /// implementation. 7 | pub fn divide(numerator: u16, divisor: u16) -> u32 { 8 | let shift = divisor.leading_zeros(); 9 | 10 | let n = (numerator as u64) << shift; 11 | let d = divisor << shift; 12 | 13 | let reciprocal = reciprocal(d) as u64; 14 | 15 | let res = (n * reciprocal + 0x8000) >> 16; 16 | 17 | if res <= 0x1ffff { 18 | res as u32 19 | } else { 20 | 0x1ffff 21 | } 22 | } 23 | 24 | fn reciprocal(d: u16) -> u32 { 25 | let index = ((d & 0x7fff) + 0x40) >> 7; 26 | 27 | let factor = UNR_TABLE[index as usize] as i32 + 0x101; 28 | 29 | let d = (d | 0x8000) as i32; 30 | 31 | let tmp = ((d * -factor) + 0x80) >> 8; 32 | 33 | let r = ((factor * (0x20000 + tmp)) + 0x80) >> 8; 34 | 35 | r as u32 36 | } 37 | 38 | // Unsigned Newtown-Raphson look up table 39 | const UNR_TABLE: [u8; 0x101] = [ 40 | 0xff, 0xfd, 0xfb, 0xf9, 0xf7, 0xf5, 0xf3, 0xf1, 41 | 0xef, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe3, 42 | 0xe1, 0xdf, 0xdd, 0xdc, 0xda, 0xd8, 0xd6, 0xd5, 43 | 0xd3, 0xd1, 0xd0, 0xce, 0xcd, 0xcb, 0xc9, 0xc8, 44 | 0xc6, 0xc5, 0xc3, 0xc1, 0xc0, 0xbe, 0xbd, 0xbb, 45 | 0xba, 0xb8, 0xb7, 0xb5, 0xb4, 0xb2, 0xb1, 0xb0, 46 | 0xae, 0xad, 0xab, 0xaa, 0xa9, 0xa7, 0xa6, 0xa4, 47 | 0xa3, 0xa2, 0xa0, 0x9f, 0x9e, 0x9c, 0x9b, 0x9a, 48 | 0x99, 0x97, 0x96, 0x95, 0x94, 0x92, 0x91, 0x90, 49 | 0x8f, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x87, 0x86, 50 | 0x85, 0x84, 0x83, 0x82, 0x81, 0x7f, 0x7e, 0x7d, 51 | 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x75, 0x74, 52 | 0x73, 0x72, 0x71, 0x70, 0x6f, 0x6e, 0x6d, 0x6c, 53 | 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 54 | 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5d, 55 | 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 56 | 0x54, 0x53, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, 57 | 0x4d, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x48, 58 | 0x47, 0x46, 0x45, 0x44, 0x43, 0x43, 0x42, 0x41, 59 | 0x40, 0x3f, 0x3f, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 60 | 0x3a, 0x39, 0x39, 0x38, 0x37, 0x36, 0x36, 0x35, 61 | 0x34, 0x33, 0x33, 0x32, 0x31, 0x31, 0x30, 0x2f, 62 | 0x2e, 0x2e, 0x2d, 0x2c, 0x2c, 0x2b, 0x2a, 0x2a, 63 | 0x29, 0x28, 0x28, 0x27, 0x26, 0x26, 0x25, 0x24, 64 | 0x24, 0x23, 0x22, 0x22, 0x21, 0x20, 0x20, 0x1f, 65 | 0x1e, 0x1e, 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 66 | 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 67 | 0x15, 0x14, 0x14, 0x13, 0x12, 0x12, 0x11, 0x11, 68 | 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 69 | 0x0c, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 70 | 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, 71 | 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 72 | 0x00, 73 | ]; 74 | 75 | 76 | /// Unsigned Newtown-Raphson table generation, based on the No$ docs 77 | #[test] 78 | fn validate_unr_table() { 79 | for i in 0..0x100u32 { 80 | let v = (0x40000 / (i + 0x100) + 1) / 2 - 0x101; 81 | 82 | assert!(UNR_TABLE[i as usize] == v as u8); 83 | } 84 | 85 | assert!(UNR_TABLE[0xff] == UNR_TABLE[0x100]); 86 | } 87 | 88 | #[test] 89 | fn test_divider() { 90 | // Tested against mednafen's "Divide" function's output. We only 91 | // reach the division if numerator < (divisor * 2). 92 | assert!(divide(0, 1) == 0); 93 | assert!(divide(0, 1234) == 0); 94 | assert!(divide(1, 1) == 0x10000); 95 | assert!(divide(2, 2) == 0x10000); 96 | assert!(divide(0xffff, 0xffff) == 0xffff); 97 | assert!(divide(0xffff, 0xfffe) == 0x10000); 98 | assert!(divide(1, 2) == 0x8000); 99 | assert!(divide(1, 3) == 0x5555); 100 | assert!(divide(5, 6) == 0xd555); 101 | assert!(divide(1, 4) == 0x4000); 102 | assert!(divide(10, 40) == 0x4000); 103 | assert!(divide(0xf00, 0xbeef) == 0x141d); 104 | assert!(divide(9876, 8765) == 0x12072); 105 | assert!(divide(200, 10000) == 0x51f); 106 | assert!(divide(0xffff, 0x8000) == 0x1fffe); 107 | assert!(divide(0xe5d7, 0x72ec) == 0x1ffff); 108 | } 109 | -------------------------------------------------------------------------------- /src/tracer.rs: -------------------------------------------------------------------------------- 1 | //! Interface used to log internal variables in order to generate 2 | //! traces 3 | 4 | use std::collections::HashMap; 5 | 6 | pub type ValueType = u32; 7 | pub type ValueSize = u8; 8 | 9 | /// Underlying type of every logged value. Since we use a `u32` we 10 | /// only support variables up to 32bits for now. The 2nd parameter is 11 | /// the size of the value in bits. 12 | #[derive(Copy, Clone)] 13 | pub struct SizedValue(pub ValueType, pub ValueSize); 14 | 15 | impl From for SizedValue { 16 | fn from(v: bool) -> SizedValue { 17 | SizedValue(v as ValueType, 1) 18 | } 19 | } 20 | 21 | impl From for SizedValue { 22 | fn from(v: u8) -> SizedValue { 23 | SizedValue(v as ValueType, 8) 24 | } 25 | } 26 | 27 | impl From for SizedValue { 28 | fn from(v: u16) -> SizedValue { 29 | SizedValue(v as ValueType, 16) 30 | } 31 | } 32 | 33 | impl From for SizedValue { 34 | fn from(v: u32) -> SizedValue { 35 | SizedValue(v as ValueType, 32) 36 | } 37 | } 38 | 39 | pub struct Variable { 40 | size: ValueSize, 41 | /// Log for this variable: `(date, value)` 42 | log: Vec<(u64, ValueType)>, 43 | } 44 | 45 | impl Variable { 46 | fn new(size: ValueSize) -> Variable { 47 | Variable { 48 | size: size, 49 | log: Vec::new(), 50 | } 51 | } 52 | 53 | pub fn size(&self) -> ValueSize { 54 | self.size 55 | } 56 | 57 | pub fn log(&self) -> &Vec<(u64, ValueType)> { 58 | &self.log 59 | } 60 | } 61 | 62 | pub struct Module { 63 | /// Variables, indexed by name. 64 | variables: HashMap<&'static str, Variable>, 65 | } 66 | 67 | impl Module { 68 | #[cfg(feature = "trace")] 69 | fn new() -> Module { 70 | Module { 71 | variables: HashMap::new(), 72 | } 73 | } 74 | 75 | pub fn variables(&self) -> &HashMap<&'static str, Variable> { 76 | &self.variables 77 | } 78 | 79 | pub fn trace>(&mut self, 80 | date: u64, 81 | name: &'static str, 82 | sized_value: V) { 83 | let SizedValue(value, size) = sized_value.into(); 84 | 85 | let var = self.variables.entry(name).or_insert(Variable::new(size)); 86 | 87 | if var.size != size { 88 | panic!("Incoherent size for variable {}: got {} and {}", 89 | name, var.size, size); 90 | } 91 | 92 | if let Some(&(last_date, last_value)) = var.log.last() { 93 | if last_date >= date { 94 | panic!("Got out-of-order events for {} ({} >= {})", 95 | name, last_date, date); 96 | } 97 | 98 | if last_value == value { 99 | // No value change 100 | return; 101 | } 102 | } 103 | 104 | var.log.push((date, value)); 105 | } 106 | } 107 | 108 | #[cfg(feature = "trace")] 109 | pub struct Tracer { 110 | /// Modules, indexed by name 111 | modules: HashMap<&'static str, Module>, 112 | } 113 | 114 | #[cfg(feature = "trace")] 115 | impl Tracer { 116 | fn new() -> Tracer { 117 | Tracer { 118 | modules: HashMap::new(), 119 | } 120 | } 121 | 122 | fn module_mut(&mut self, name: &'static str) -> &mut Module { 123 | self.modules.entry(name).or_insert(Module::new()) 124 | } 125 | 126 | /// Reset the tracer and return the content of the previous trace 127 | fn remove_trace(&mut self) -> HashMap<&'static str, Module> { 128 | let mut swap = HashMap::new(); 129 | 130 | ::std::mem::swap(&mut self.modules, &mut swap); 131 | 132 | swap 133 | } 134 | } 135 | 136 | /// Global logger instance 137 | #[cfg(feature = "trace")] 138 | lazy_static! { 139 | static ref LOGGER: ::std::sync::Mutex = { 140 | ::std::sync::Mutex::new(Tracer::new()) 141 | }; 142 | } 143 | 144 | #[cfg(feature = "trace")] 145 | pub fn remove_trace() -> HashMap<&'static str, Module> { 146 | let mut logger = LOGGER.lock().unwrap(); 147 | 148 | logger.remove_trace() 149 | } 150 | 151 | #[cfg(not(feature = "trace"))] 152 | pub fn remove_trace() -> HashMap<&'static str, Module> { 153 | HashMap::new() 154 | } 155 | 156 | #[cfg(feature = "trace")] 157 | pub fn module_tracer(name: &'static str, f: F) 158 | where F: FnOnce(&mut Module) { 159 | 160 | let mut logger = LOGGER.lock().unwrap(); 161 | 162 | let module = logger.module_mut(name); 163 | 164 | f(module); 165 | } 166 | 167 | #[cfg(not(feature = "trace"))] 168 | #[inline(always)] 169 | pub fn module_tracer(_name: &'static str, _f: F) 170 | where F: FnOnce(&mut Module) { 171 | // NOP 172 | } 173 | -------------------------------------------------------------------------------- /src/cpu/cop0.rs: -------------------------------------------------------------------------------- 1 | use interrupt::InterruptState; 2 | 3 | /// Coprocessor 0: System control 4 | #[derive(RustcDecodable, RustcEncodable)] 5 | pub struct Cop0 { 6 | /// Cop0 register 12: Status register 7 | sr: u32, 8 | /// Cop0 register 13: Cause register 9 | cause: u32, 10 | /// Cop0 register 14: Exception PC 11 | epc: u32, 12 | } 13 | 14 | impl Cop0 { 15 | 16 | pub fn new() -> Cop0 { 17 | Cop0 { 18 | sr: 0, 19 | cause: 0, 20 | epc: 0, 21 | } 22 | } 23 | 24 | pub fn sr(&self) -> u32 { 25 | self.sr 26 | } 27 | 28 | pub fn set_sr(&mut self, sr: u32) { 29 | self.sr = sr; 30 | } 31 | 32 | pub fn set_cause(&mut self, v: u32) { 33 | // Bits [9:8] are writeable and can be used to trigger a 34 | // software interrupt 35 | self.cause &= !0x300; 36 | self.cause |= v & 0x300; 37 | } 38 | 39 | /// Retreive the value of the CAUSE register. We need the 40 | /// InterruptState because bit 10 is wired to the current external 41 | /// interrupt (no latch, ack'ing the interrupt in the external 42 | /// controller resets the value in this register) . 43 | pub fn cause(&self, irq_state: InterruptState) -> u32 { 44 | self.cause | ((irq_state.active() as u32) << 10) 45 | } 46 | 47 | pub fn epc(&self) -> u32 { 48 | self.epc 49 | } 50 | 51 | pub fn cache_isolated(&self) -> bool { 52 | self.sr & 0x10000 != 0 53 | } 54 | 55 | /// Update SR, CAUSE and EPC when an exception is 56 | /// triggered. Returns the address of the exception handler. 57 | pub fn enter_exception(&mut self, 58 | cause: Exception, 59 | pc: u32, 60 | in_delay_slot: bool) -> u32 { 61 | // Shift bits [5:0] of `SR` two places to the left. Those bits 62 | // are three pairs of Interrupt Enable/User Mode bits behaving 63 | // like a stack 3 entries deep. Entering an exception pushes a 64 | // pair of zeroes by left shifting the stack which disables 65 | // interrupts and puts the CPU in kernel mode. The original 66 | // third entry is discarded (it's up to the kernel to handle 67 | // more than two recursive exception levels). 68 | let mode = self.sr & 0x3f; 69 | 70 | self.sr &= !0x3f; 71 | self.sr |= (mode << 2) & 0x3f; 72 | 73 | // Update `CAUSE` register with the exception code (bits 74 | // [6:2]) 75 | self.cause &= !0x7c; 76 | self.cause |= (cause as u32) << 2; 77 | 78 | if in_delay_slot { 79 | // When an exception occurs in a delay slot `EPC` points 80 | // to the branch instruction and bit 31 of `CAUSE` is set. 81 | self.epc = pc.wrapping_sub(4); 82 | self.cause |= 1 << 31; 83 | } else { 84 | self.epc = pc; 85 | self.cause &= !(1 << 31); 86 | } 87 | 88 | // The address of the exception handler address depends on the 89 | // value of the BEV bit in SR 90 | match self.sr & (1 << 22) != 0 { 91 | true => 0xbfc00180, 92 | false => 0x80000080, 93 | } 94 | } 95 | 96 | /// The counterpart to "enter_exception": shift SR's mode back 97 | /// into place. Doesn't touch CAUSE or EPC however. 98 | pub fn return_from_exception(&mut self) { 99 | let mode = self.sr & 0x3f; 100 | 101 | // Bits [5:4] (the third and last mode in the stack) remains 102 | // untouched and is therefore duplicated in the 2nd entry. 103 | self.sr &= !0xf; 104 | self.sr |= mode >> 2; 105 | } 106 | 107 | /// Return true if the interrupts are enabled on the CPU (SR 108 | /// "Current Interrupt Enable" bit is set) 109 | fn irq_enabled(&self) -> bool { 110 | self.sr & 1 != 0 111 | } 112 | 113 | /// Return true if an interrupt (either software or hardware) is 114 | /// pending 115 | pub fn irq_active(&self, irq_state: InterruptState) -> bool { 116 | let cause = self.cause(irq_state); 117 | 118 | // Bits [8:9] of CAUSE contain the two software interrupts 119 | // (that the software can use by writing to the CAUSE 120 | // register) while bit 10 is wired to the external interrupt's 121 | // state. They can be individually masked using SR's bits 122 | // [8:10] 123 | let pending = (cause & self.sr) & 0x700; 124 | 125 | // Finally bit 0 of SR can be used to mask interrupts globally 126 | // on the CPU so we must check that. 127 | self.irq_enabled() && pending != 0 128 | } 129 | } 130 | 131 | /// Exception types (as stored in the `CAUSE` register) 132 | #[derive(Clone, Copy, Debug)] 133 | pub enum Exception { 134 | /// Interrupt Request 135 | Interrupt = 0x0, 136 | /// Address error on load 137 | LoadAddressError = 0x4, 138 | /// Address error on store 139 | StoreAddressError = 0x5, 140 | /// System call (caused by the SYSCALL opcode) 141 | SysCall = 0x8, 142 | /// Breakpoint (caused by the BREAK opcode) 143 | Break = 0x9, 144 | /// CPU encountered an unknown instruction 145 | IllegalInstruction = 0xa, 146 | /// Unsupported coprocessor operation 147 | CoprocessorError = 0xb, 148 | /// Arithmetic overflow 149 | Overflow = 0xc, 150 | } 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/simias/rustation.svg)](https://travis-ci.org/simias/rustation) 2 | 3 | # This project is no longer being developed. 4 | 5 | I decided to restart from scratch to fix some architectural flaws in Rustation. 6 | Some of the code has been imported from this project, other bits have been 7 | thoroughly rewritten. The new repository is 8 | [rustation-ng](https://gitlab.com/flio/rustation-ng/). It's now more advanced 9 | than this project, with proper sound support and generally more accurate 10 | emulation. 11 | 12 | One significant difference between the two projects is than rustation-ng dropped 13 | the GL renderer in favor of a threaded software one. The GL renderer of 14 | Rustation has been ported to C++ and has been integrated in the Beetle PSX 15 | emulator however, so it's not completely dead. 16 | 17 | # Rustation PlayStation emulator 18 | 19 | ![Rustation logo](https://raw.githubusercontent.com/wiki/simias/rustation/images/logo.png) 20 | 21 | PlayStation emulator in the Rust programing language. 22 | 23 | This repository only contains the source code for the core of the 24 | emulator. The OpenGL renderer and the libretro interface is is the 25 | [rustation-libretro](https://github.com/simias/rustation-libretro) 26 | repository. 27 | 28 | The focus of this emulator is to write code that's clean, accurate and 29 | hopefully easy to understand. There's no plugin infrastructure, the 30 | emulator is monolithic. 31 | 32 | Performance is pretty poor at the moment but it should be enough to 33 | run close to realtime on a modern CPU. 34 | 35 | For the time being it can only boot a few games. Crash Bandicoot 36 | (Japanese version) is mostly playable, although I've had random 37 | crashes. Some other games (like Spyro) freeze after or during the 38 | intro. 39 | 40 | If you have any questions, in particular if something in the code is 41 | not clear or properly commented don't hesitate to fill an issue. 42 | 43 | I also created a [/psx/ board on 8chan](https://8ch.net/psx/) if you 44 | prefer something less formal to discuss this emulator and all things 45 | PlayStation. We'll see if this turns out to be a good idea... 46 | 47 | ## Currently implemented (even partially) 48 | 49 | ![Crash Bandicoot (Japan)](https://raw.githubusercontent.com/wiki/simias/rustation/images/crash_bandicoot-level1.png) 50 | 51 | * CPU 52 | * Basic GTE support (ported from mednafen PSX) 53 | * Instruction cache 54 | * Interrupts 55 | * Basic GPU (no semi-transparency or mask bit emulation) 56 | * Timers (incomplete) 57 | * DMA 58 | * Debugger 59 | * CDROM controller (missing many commands) 60 | * Gamepad controller (only digital pad for now) 61 | 62 | ## Todo list 63 | 64 | * Many things in the GPU 65 | * MDEC 66 | * SPU 67 | * Memory card 68 | * CPU pipeline emulation 69 | * More accurate timings 70 | * Many, many other things... 71 | 72 | ## Build 73 | 74 | You'll need [Rust and its package manager Cargo](https://www.rust-lang.org/), 75 | [SDL2](https://www.libsdl.org/download-2.0.php) and a PlayStation 76 | BIOS. The emulator is mainly tested with BIOS version `SCPH1001` whose 77 | SHA-1 is `10155d8d6e6e832d6ea66db9bc098321fb5e8ebf`. 78 | 79 | You should then be able to build the emulator with: 80 | 81 | ``` 82 | cargo build --release 83 | ``` 84 | 85 | Don't forget the `--release` flag in order to turn optimizations 86 | on. Without them the resulting binary will be absurdly slow. 87 | 88 | If the build is succesful you can run the emulator using: 89 | 90 | ``` 91 | cargo run --release /path/to/SCPH1001.BIN 92 | ``` 93 | 94 | For Windows check issue [#12](https://github.com/simias/rustation/issues/12). 95 | 96 | Use the `Escape` key to exit the emulator, `Pause/Break` to "break" into the 97 | debugger, the emulator will then listen on TCP port `9001` for a GDB 98 | connection. 99 | 100 | ## Debugger 101 | 102 | In order to debug you'll need a GDB targetting 103 | `mipsel-unknown-elf`. Once the emulator is running press the 104 | `Pause/Break` key to trigger the debugger and then connect GDB to it 105 | using (at the gdb command prompt): 106 | 107 | `target remote localhost:9001` 108 | 109 | GDB might complain about not finding symbols or the boundaries of the 110 | current function but you can ignore that. From then you should be able 111 | to use the familiar [GDB commands](https://sourceware.org/gdb/onlinedocs/gdb/) 112 | to debug the live emulator. 113 | 114 | A few examples: 115 | 116 | ``` 117 | # Dump the CPU registers 118 | info registers 119 | # Disassemble 20 instructions around PC 120 | disassemble $pc-40,+80 121 | # Display word at address 0x1f801814 (GPU status) 122 | x/x 0x1f801814 123 | # Add code breakpoint at address 0x00004588 124 | break *0x00004588 125 | # Add write watchpoint at address 0x1f801070 (IRQ ack) 126 | watch *0x1f801070 127 | # Step over a single instruction 128 | stepi 129 | # Continue until a break/watchpoint is reached (or Pause/Break is pressed) 130 | continue 131 | ``` 132 | 133 | The debugger support is pretty experimental and quircky but it works 134 | for basic debugging needs. 135 | 136 | ## Guide 137 | 138 | I'm also attempting to document the emulator writing process in a 139 | LaTeX document available in the 140 | [psx-guide repository](https://github.com/simias/psx-guide). It's 141 | generally lagging behind the actual code but I'll try to update it as 142 | often as possible. 143 | 144 | ## Resources 145 | 146 | I try to cite all of my sources in the guide above but I'm mainly 147 | using the priceless [No$ PSX 148 | specifications](http://problemkaputt.de/psx-spx.htm) as well as 149 | [mednafen](http://mednafen.fobby.net/)'s source code when I feel like 150 | cheating. 151 | 152 | I also run tests on the real hardware and store them in the 153 | [psx-hardware-tests repository](https://github.com/simias/psx-hardware-tests/tree/master/tests). 154 | -------------------------------------------------------------------------------- /src/bios/mod.rs: -------------------------------------------------------------------------------- 1 | use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; 2 | 3 | use memory::Addressable; 4 | use cdrom::disc::Region; 5 | 6 | use self::db::Metadata; 7 | 8 | pub mod db; 9 | 10 | /// BIOS image 11 | pub struct Bios { 12 | /// BIOS memory. Boxed in order not to overflow the stack at the 13 | /// construction site. Might change once "placement new" is 14 | /// available. 15 | data: Box<[u8; BIOS_SIZE]>, 16 | metadata: &'static Metadata, 17 | } 18 | 19 | impl Bios { 20 | 21 | /// Create a BIOS image from `binary` and attempt to match it with 22 | /// an entry in the database. If no match can be found return 23 | /// `None`. 24 | pub fn new(binary: Box<[u8; BIOS_SIZE]>) -> Option { 25 | match db::lookup_blob(&*binary) { 26 | Some(metadata) => Some(Bios { 27 | data: binary, 28 | metadata: metadata, 29 | }), 30 | None => None, 31 | } 32 | } 33 | 34 | /// Generate a dummy BIOS that won't work, used for 35 | /// deserialization and running unit tests 36 | pub fn dummy() -> Bios { 37 | let mut bios = 38 | Bios { 39 | data: box_array![0; BIOS_SIZE], 40 | metadata: &DUMMY_METADATA, 41 | }; 42 | 43 | // Store `0x7badb105` (an invalid instruction) in the BIOS 44 | // for troubleshooting. 45 | for (i, b) in bios.data.iter_mut().enumerate() { 46 | *b = (0x7badb105 >> ((i % 4) * 2)) as u8; 47 | } 48 | 49 | bios 50 | } 51 | 52 | /// Attempt to modify the BIOS ROM to remove the call to the code 53 | /// responsible for the boot logo animations (SCEx/PS) and 54 | /// directly boot the game. This can break some games! Returns 55 | /// `Ok(())` if the code was patched, `Err(())` if we don't know 56 | /// how to hack this particular BIOS. 57 | pub fn patch_boot_animation(&mut self) -> Result<(), ()> { 58 | // Set the logo jump to `0` (NOP) 59 | self.patch_animation_jump_hook(0) 60 | } 61 | 62 | /// Attempt to modify the BIOS ROM to replace the call to the code 63 | /// responsible for the boot logo animations by the provided 64 | /// instruction. 65 | pub fn patch_animation_jump_hook(&mut self, 66 | instruction: u32) -> Result<(), ()> { 67 | match self.metadata.animation_jump_hook { 68 | Some(h) => { 69 | let h = h as usize; 70 | 71 | self.data[h] = instruction as u8; 72 | self.data[h + 1] = (instruction >> 8) as u8; 73 | self.data[h + 2] = (instruction >> 16) as u8; 74 | self.data[h + 3] = (instruction >> 24) as u8; 75 | 76 | Ok(()) 77 | } 78 | None => Err(()) 79 | } 80 | } 81 | 82 | /// Attempt to modify the BIOS ROM to enable the debug UART 83 | /// output. Returns `Err(())` if we couldn't patch the BIOS. 84 | pub fn enable_debug_uart(&mut self) -> Result<(), ()> { 85 | match self.metadata.patch_debug_uart { 86 | Some(patch) => { 87 | patch(self); 88 | Ok(()) 89 | }, 90 | None => Err(()), 91 | } 92 | } 93 | 94 | /// fetch the little endian value at `offset` 95 | pub fn load(&self, offset: u32) -> u32 { 96 | let offset = offset as usize; 97 | 98 | let mut r = 0; 99 | 100 | for i in 0..T::size() as usize { 101 | r |= (self.data[offset + i] as u32) << (8 * i) 102 | } 103 | 104 | r 105 | } 106 | 107 | /// Return a static pointer to the BIOS's Metadata 108 | pub fn metadata(&self) -> &'static Metadata { 109 | self.metadata 110 | } 111 | } 112 | 113 | impl Encodable for Bios { 114 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 115 | // We don't store the full BIOS image in the savestate, mainly 116 | // because I want to be able to share and distribute 117 | // savestates without having to worry about legal 118 | // implications. Let's just serialize the checksum to make 119 | // sure we use the correct BIOS when loading the savestate. 120 | 121 | let sha256 = &self.metadata.sha256; 122 | 123 | s.emit_seq(sha256.len(), |s| { 124 | for (i, b) in sha256.iter().enumerate() { 125 | try!(s.emit_seq_elt(i, |s| b.encode(s))); 126 | } 127 | Ok(()) 128 | }) 129 | } 130 | } 131 | 132 | impl Decodable for Bios { 133 | fn decode(d: &mut D) -> Result { 134 | d.read_seq(|d, len| { 135 | let mut sha256 = [0; 32]; 136 | 137 | if len != sha256.len() { 138 | return Err(d.error("wrong BIOM checksum length")); 139 | } 140 | 141 | for (i, b) in sha256.iter_mut().enumerate() { 142 | *b = try!(d.read_seq_elt(i, |d| Decodable::decode(d))) 143 | } 144 | 145 | let meta = 146 | match db::lookup_sha256(&sha256) { 147 | Some(m) => m, 148 | None => return Err(d.error("unknown BIOS checksum")), 149 | }; 150 | 151 | // Create an "empty" BIOS instance, only referencing the 152 | // metadata. It's up to the caller to fill the blanks. 153 | let mut bios = Bios::dummy(); 154 | 155 | bios.metadata = meta; 156 | 157 | Ok(bios) 158 | }) 159 | } 160 | } 161 | 162 | /// Dummy metadata used as a placeholder for dummy BIOS instances 163 | static DUMMY_METADATA: Metadata = 164 | Metadata { 165 | sha256: [0xff; 32], 166 | version_major: 0, 167 | version_minor: 0, 168 | region: Region::NorthAmerica, 169 | known_bad: true, 170 | animation_jump_hook: None, 171 | patch_debug_uart: None, 172 | }; 173 | 174 | /// BIOS images are always 512KB in length 175 | pub const BIOS_SIZE: usize = 512 * 1024; 176 | -------------------------------------------------------------------------------- /src/serializer.rs: -------------------------------------------------------------------------------- 1 | //! Helper macros for serialization purposes. 2 | 3 | /// Create a wrapper type around a function pointer in order to be 4 | /// able to serialize it. The new type implements Deref an DerefMut 5 | /// for convenience. In order to be able to encode and decode the 6 | /// function this macro needs to be given the *exhaustive* list of all 7 | /// the methods that can be stored it in, otherwise the 8 | /// encoding/decoding will fail for unknown functions: 9 | /// 10 | /// ``` 11 | /// # #[macro_use] extern crate rustation; 12 | /// # #[macro_use] extern crate rustc_serialize; 13 | /// # fn main() { 14 | /// callback!( 15 | /// struct MyHandler(fn(u32) -> bool) { 16 | /// foo, 17 | /// bar, 18 | /// baz, 19 | /// }); 20 | /// 21 | /// fn foo(_: u32) -> bool { true } 22 | /// fn bar(_: u32) -> bool { false } 23 | /// fn baz(v: u32) -> bool { v == 0 } 24 | /// # } 25 | /// ``` 26 | #[macro_export] 27 | macro_rules! callback { 28 | (struct $st:ident ($proto:ty) { 29 | $($ptr:expr),+$(,)* 30 | }) => ( 31 | #[derive(Copy)] 32 | struct $st($proto); 33 | 34 | impl Clone for $st { 35 | fn clone(&self) -> Self { 36 | *self 37 | } 38 | } 39 | 40 | // Implement Deref to make accessing the underlying function 41 | // pointer more convenient 42 | impl ::std::ops::Deref for $st { 43 | type Target = $proto; 44 | 45 | fn deref(&self) -> &$proto { 46 | &self.0 47 | } 48 | } 49 | 50 | // Implement DerefMut to make setting the underlying function 51 | // pointer more convenient 52 | impl ::std::ops::DerefMut for $st { 53 | fn deref_mut(&mut self) -> &mut $proto { 54 | &mut self.0 55 | } 56 | } 57 | 58 | impl ::rustc_serialize::Encodable for $st { 59 | fn encode(&self, s: &mut S) -> Result<(), S::Error> 60 | where S: ::rustc_serialize::Encoder { 61 | let address = self.0 as usize; 62 | 63 | let lut = [ 64 | $(($ptr as usize, stringify!($ptr))),+, 65 | ]; 66 | 67 | for &(a, n) in lut.iter() { 68 | if address == a { 69 | return s.emit_str(n) 70 | } 71 | } 72 | 73 | panic!("Unexpected method pointer {:x}", address); 74 | } 75 | } 76 | 77 | impl ::rustc_serialize::Decodable for $st { 78 | fn decode(d: &mut D) -> Result<$st, D::Error> 79 | where D: ::rustc_serialize::Decoder { 80 | 81 | let symbol = try!(d.read_str()); 82 | 83 | let lut = [ 84 | $(($ptr as $proto, stringify!($ptr))),+, 85 | ]; 86 | 87 | for &(f, n) in lut.iter() { 88 | if symbol == n { 89 | return Ok($st(f)); 90 | } 91 | } 92 | 93 | Err(d.error("Unknown callback")) 94 | } 95 | } 96 | ); 97 | } 98 | 99 | /// Create a wrapper type around an array in order to be able to 100 | /// serialize it. The new type implements Deref an DerefMut for 101 | /// convenience. The element type must implement 102 | /// `std::default::Default`: 103 | /// 104 | /// ``` 105 | /// # #[macro_use] extern crate rustation; 106 | /// # #[macro_use] extern crate rustc_serialize; 107 | /// # fn main() { 108 | /// buffer!(struct MyBuffer([u8; 1024])); 109 | /// 110 | /// let mut buf = MyBuffer::new(); 111 | /// assert!(buf[55] == 0); 112 | /// buf[55] += 1; 113 | /// assert!(buf[55] == 1); 114 | /// # } 115 | /// ``` 116 | #[macro_export] 117 | macro_rules! buffer { 118 | (struct $st:ident ([$elem: ty; $len: expr])) => ( 119 | struct $st([$elem; $len]); 120 | 121 | impl $st { 122 | /// Build a new $st using the `Default` constructor 123 | fn new() -> $st { 124 | ::std::default::Default::default() 125 | } 126 | } 127 | 128 | impl ::std::default::Default for $st { 129 | fn default() -> $st { 130 | $st([::std::default::Default::default(); $len]) 131 | } 132 | } 133 | 134 | // Implement Deref to make accessing the underlying function 135 | // pointer more convenient 136 | impl ::std::ops::Deref for $st { 137 | type Target = [$elem; $len]; 138 | 139 | fn deref(&self) -> &[$elem; $len] { 140 | &self.0 141 | } 142 | } 143 | 144 | // Implement DerefMut to make setting the underlying function 145 | // pointer more convenient 146 | impl ::std::ops::DerefMut for $st { 147 | fn deref_mut(&mut self) -> &mut [$elem; $len] { 148 | &mut self.0 149 | } 150 | } 151 | 152 | impl ::rustc_serialize::Encodable for $st { 153 | fn encode(&self, s: &mut S) -> Result<(), S::Error> 154 | where S: ::rustc_serialize::Encoder { 155 | 156 | s.emit_seq($len, |s| { 157 | for (i, b) in self.0.iter().enumerate() { 158 | try!(s.emit_seq_elt(i, |s| b.encode(s))); 159 | } 160 | Ok(()) 161 | }) 162 | } 163 | } 164 | 165 | impl ::rustc_serialize::Decodable for $st { 166 | fn decode(d: &mut D) -> Result<$st, D::Error> 167 | where D: ::rustc_serialize::Decoder { 168 | 169 | use ::rustc_serialize::Decodable; 170 | 171 | d.read_seq(|d, len| { 172 | if len != $len { 173 | return Err(d.error("wrong buffer length")); 174 | } 175 | 176 | let mut b = $st::new(); 177 | 178 | for (i, b) in b.0.iter_mut().enumerate() { 179 | *b = try!(d.read_seq_elt(i, Decodable::decode)) 180 | } 181 | 182 | Ok(b) 183 | }) 184 | } 185 | } 186 | ); 187 | } 188 | -------------------------------------------------------------------------------- /src/memory/ram.rs: -------------------------------------------------------------------------------- 1 | use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; 2 | 3 | use super::Addressable; 4 | 5 | /// RAM 6 | pub struct Ram { 7 | /// RAM buffer. Boxed in order not to overflow the stack at the 8 | /// construction site. Might change once "placement new" is 9 | /// available. 10 | data: Box<[u8; RAM_SIZE]> 11 | } 12 | 13 | impl Ram { 14 | 15 | /// Instantiate main RAM with garbage values 16 | pub fn new() -> Ram { 17 | 18 | Ram { data: box_array![0xca; RAM_SIZE] } 19 | } 20 | 21 | /// Fetch the little endian value at `offset` 22 | pub fn load(&self, offset: u32) -> u32 { 23 | // The two MSB are ignored, the 2MB RAM is mirorred four times 24 | // over the first 8MB of address space 25 | let offset = (offset & 0x1fffff) as usize; 26 | 27 | let mut v = 0; 28 | 29 | for i in 0..T::size() as usize { 30 | v |= (self.data[offset + i] as u32) << (i * 8) 31 | } 32 | 33 | v 34 | } 35 | 36 | /// Store the 32bit little endian word `val` into `offset` 37 | pub fn store(&mut self, offset: u32, val: u32) { 38 | // The two MSB are ignored, the 2MB RAM is mirorred four times 39 | // over the first 8MB of address space 40 | let offset = (offset & 0x1fffff) as usize; 41 | 42 | for i in 0..T::size() as usize { 43 | self.data[offset + i] = (val >> (i * 8)) as u8; 44 | } 45 | } 46 | } 47 | 48 | impl Encodable for Ram { 49 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 50 | s.emit_seq(self.data.len(), |s| { 51 | for (i, b) in self.data.iter().enumerate() { 52 | try!(s.emit_seq_elt(i, |s| b.encode(s))); 53 | } 54 | Ok(()) 55 | }) 56 | } 57 | } 58 | 59 | impl Decodable for Ram { 60 | fn decode(d: &mut D) -> Result { 61 | d.read_seq(|d, len| { 62 | if len != RAM_SIZE { 63 | return Err(d.error("wrong RAM length")); 64 | } 65 | 66 | let mut ram = Ram::new(); 67 | 68 | for (i, b) in ram.data.iter_mut().enumerate() { 69 | *b = try!(d.read_seq_elt(i, Decodable::decode)) 70 | } 71 | 72 | Ok(ram) 73 | }) 74 | } 75 | } 76 | 77 | /// ScratchPad memory 78 | pub struct ScratchPad { 79 | data: [u8; SCRATCH_PAD_SIZE] 80 | } 81 | 82 | impl ScratchPad { 83 | 84 | /// Instantiate scratchpad with garbage values 85 | pub fn new() -> ScratchPad { 86 | ScratchPad { data: [0xdb; SCRATCH_PAD_SIZE] } 87 | } 88 | 89 | /// Fetch the little endian value at `offset` 90 | pub fn load(&self, offset: u32) -> u32 { 91 | let offset = offset as usize; 92 | 93 | let mut v = 0; 94 | 95 | for i in 0..T::size() as usize { 96 | v |= (self.data[offset + i] as u32) << (i * 8) 97 | } 98 | 99 | v 100 | } 101 | 102 | /// Store the 32bit little endian word `val` into `offset` 103 | pub fn store(&mut self, offset: u32, val: u32) { 104 | let offset = offset as usize; 105 | 106 | for i in 0..T::size() as usize { 107 | self.data[offset + i] = (val >> (i * 8)) as u8; 108 | } 109 | } 110 | } 111 | 112 | impl Encodable for ScratchPad { 113 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 114 | s.emit_seq(SCRATCH_PAD_SIZE, |s| { 115 | for i in 0..SCRATCH_PAD_SIZE { 116 | try!(s.emit_seq_elt(i, |s| self.data[i].encode(s))); 117 | } 118 | Ok(()) 119 | }) 120 | } 121 | } 122 | 123 | impl Decodable for ScratchPad { 124 | fn decode(d: &mut D) -> Result { 125 | d.read_seq(|d, len| { 126 | if len != SCRATCH_PAD_SIZE { 127 | return Err(d.error("wrong SCRATCH_PAD length")); 128 | } 129 | 130 | let mut ram = ScratchPad::new(); 131 | 132 | for (i, b) in ram.data.iter_mut().enumerate() { 133 | *b = try!(d.read_seq_elt(i, Decodable::decode)) 134 | } 135 | 136 | Ok(ram) 137 | }) 138 | } 139 | } 140 | 141 | /// Main PlayStation RAM: 2Megabytes 142 | const RAM_SIZE: usize = 2 * 1024 * 1024; 143 | 144 | /// ScatchPad (data cache used as fast RAM): 1Kilobyte 145 | const SCRATCH_PAD_SIZE: usize = 1024; 146 | 147 | #[test] 148 | fn ram_read() { 149 | use super::{Word, HalfWord, Byte}; 150 | 151 | let mut ram = Ram::new(); 152 | 153 | ram.store::(0, 0x12345678); 154 | ram.store::(32, 0x0abcdef0); 155 | 156 | assert!(ram.load::(0) == 0x12345678); 157 | 158 | assert!(ram.load::(32) == 0x0abcdef0); 159 | 160 | assert!(ram.load::(0) == 0x5678); 161 | assert!(ram.load::(2) == 0x1234); 162 | 163 | assert!(ram.load::(32) == 0xdef0); 164 | assert!(ram.load::(34) == 0x0abc); 165 | 166 | assert!(ram.load::(0) == 0x78); 167 | assert!(ram.load::(1) == 0x56); 168 | assert!(ram.load::(2) == 0x34); 169 | assert!(ram.load::(3) == 0x12); 170 | 171 | assert!(ram.load::(32) == 0xf0); 172 | assert!(ram.load::(33) == 0xde); 173 | assert!(ram.load::(34) == 0xbc); 174 | assert!(ram.load::(35) == 0x0a); 175 | } 176 | 177 | #[test] 178 | fn ram_write() { 179 | use super::{Word, HalfWord, Byte}; 180 | 181 | let mut ram = Ram::new(); 182 | 183 | ram.store::(32, 0x12345678); 184 | ram.store::(32, 0xabcd); 185 | assert!(ram.load::(32) == 0x1234abcd); 186 | 187 | ram.store::(32, 0x12345678); 188 | ram.store::(34, 0xabcd); 189 | assert!(ram.load::(32) == 0xabcd5678); 190 | 191 | ram.store::(32, 0x12345678); 192 | ram.store::(32, 0xab); 193 | assert!(ram.load::(32) == 0x123456ab); 194 | 195 | ram.store::(32, 0x12345678); 196 | ram.store::(33, 0xab); 197 | assert!(ram.load::(32) == 0x1234ab78); 198 | 199 | ram.store::(32, 0x12345678); 200 | ram.store::(34, 0xab); 201 | assert!(ram.load::(32) == 0x12ab5678); 202 | 203 | ram.store::(32, 0x12345678); 204 | ram.store::(35, 0xab); 205 | assert!(ram.load::(32) == 0xab345678); 206 | } 207 | -------------------------------------------------------------------------------- /src/padmemcard/gamepad.rs: -------------------------------------------------------------------------------- 1 | use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; 2 | 3 | pub struct GamePad { 4 | /// Gamepad profile. *Not* stored in the savestate. 5 | profile: Box, 6 | /// Counter keeping track of the current position in the reply 7 | /// sequence 8 | seq: u8, 9 | /// False if the pad is done processing the current command 10 | active: bool, 11 | } 12 | 13 | impl GamePad { 14 | /// Create a new disconnected GamePad 15 | pub fn disconnected() -> GamePad { 16 | GamePad { 17 | seq: 0, 18 | active: true, 19 | profile: Box::new(DisconnectedProfile), 20 | } 21 | } 22 | 23 | /// Called when the "select" line goes down. 24 | pub fn select(&mut self) { 25 | // Prepare for incomming command 26 | self.active = true; 27 | self.seq = 0; 28 | } 29 | 30 | /// The 2nd return value is the response byte. The 2nd return 31 | /// value is true if the gamepad issues a DSR pulse after the byte 32 | /// is read to notify the controller that more data can be read. 33 | pub fn send_command(&mut self, cmd: u8) -> (u8, bool) { 34 | if !self.active { 35 | return (0xff, false); 36 | } 37 | 38 | let (resp, dsr) = self.profile.handle_command(self.seq, cmd); 39 | 40 | // If we're not asserting DSR it either means that we've 41 | // encountered an error or that we have nothing else to 42 | // reply. In either case we won't be handling any more command 43 | // bytes in this transaction. 44 | self.active = dsr; 45 | 46 | self.seq += 1; 47 | 48 | (resp, dsr) 49 | } 50 | 51 | /// Return a mutable reference to the underlying gamepad Profile 52 | pub fn profile_mut(&mut self) -> &mut Profile { 53 | &mut *self.profile 54 | } 55 | 56 | pub fn set_profile(&mut self, profile: Box) { 57 | self.profile = profile 58 | } 59 | } 60 | 61 | impl Encodable for GamePad { 62 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 63 | 64 | // We don't store the Profile in the serialized data, we'll 65 | // let the frontend reset it 66 | s.emit_struct("GamePad", 2, |s| { 67 | 68 | try!(s.emit_struct_field("seq", 0, 69 | |s| self.seq.encode(s))); 70 | try!(s.emit_struct_field("active", 1, 71 | |s| self.active.encode(s))); 72 | 73 | Ok(()) 74 | }) 75 | } 76 | } 77 | 78 | impl Decodable for GamePad { 79 | fn decode(d: &mut D) -> Result { 80 | 81 | d.read_struct("GamePad", 2, |d| { 82 | let mut pad = GamePad::disconnected(); 83 | 84 | pad.seq = 85 | try!(d.read_struct_field("seq", 0, Decodable::decode)); 86 | 87 | pad.active = 88 | try!(d.read_struct_field("active", 1, Decodable::decode)); 89 | 90 | Ok(pad) 91 | }) 92 | } 93 | } 94 | 95 | /// Digital buttons on a PlayStation controller. The value assigned to 96 | /// each button is the bit position in the 16bit word returned in the 97 | /// serial protocol 98 | #[derive(Clone,Copy,Debug)] 99 | pub enum Button { 100 | Select = 0, 101 | Start = 3, 102 | DUp = 4, 103 | DRight = 5, 104 | DDown = 6, 105 | DLeft = 7, 106 | L2 = 8, 107 | R2 = 9, 108 | L1 = 10, 109 | R1 = 11, 110 | Triangle = 12, 111 | Circle = 13, 112 | Cross = 14, 113 | Square = 15, 114 | } 115 | 116 | #[derive(Clone,Copy,Debug)] 117 | pub enum ButtonState { 118 | Pressed, 119 | Released, 120 | } 121 | 122 | /// Trait used to abstract away the various controller types. 123 | pub trait Profile { 124 | /// Handle a command byte sent by the console. `seq` is the byte 125 | /// position in the current command starting with `1` since byte 126 | /// `0` is expected to always be `0x01` when addressing a 127 | /// controller and is handled at the top level. 128 | /// 129 | /// Returns a pair `(response, dsr)`. If DSR is false the 130 | /// subsequent command bytes will be ignored for the current 131 | /// transaction. 132 | fn handle_command(&mut self, seq: u8, cmd: u8) -> (u8, bool); 133 | 134 | /// Set a button's state. The function can be called several time 135 | /// in a row with the same button and the same state, it should be 136 | /// idempotent. 137 | fn set_button_state(&mut self, button: Button, state: ButtonState); 138 | } 139 | 140 | /// Dummy profile emulating an empty pad slot 141 | pub struct DisconnectedProfile; 142 | 143 | impl Profile for DisconnectedProfile { 144 | fn handle_command(&mut self, _: u8, _: u8) -> (u8, bool) { 145 | // The bus is open, no response 146 | (0xff, false) 147 | } 148 | 149 | fn set_button_state(&mut self, _: Button, _: ButtonState) { 150 | } 151 | } 152 | 153 | /// SCPH-1080: Digital gamepad. 154 | /// Full state is only two bytes since we only need one bit per 155 | /// button. 156 | pub struct DigitalProfile(u16); 157 | 158 | impl DigitalProfile { 159 | pub fn new() -> DigitalProfile { 160 | DigitalProfile(0xffff) 161 | } 162 | } 163 | 164 | impl Profile for DigitalProfile { 165 | fn handle_command(&mut self, seq: u8, cmd: u8) -> (u8, bool) { 166 | match seq { 167 | // First byte should be 0x01 if the command targets 168 | // the controller 169 | 0 => (0xff, (cmd == 0x01)), 170 | // Digital gamepad only supports command 0x42: read 171 | // buttons. 172 | // Response 0x41: we're a digital PSX controller 173 | 1 => (0x41, (cmd == 0x42)), 174 | // From then on the command byte is ignored. 175 | // Response 0x5a: 2nd controller ID byte 176 | 2 => (0x5a, true), 177 | // First button state byte: direction cross, start and 178 | // select. 179 | 3 => (self.0 as u8, true), 180 | // 2nd button state byte: shoulder buttons and "shape" 181 | // buttons. We don't asert DSR for the last byte. 182 | 4 => ((self.0 >> 8) as u8, false), 183 | // Shouldn't be reached 184 | _ => (0xff, false), 185 | } 186 | } 187 | 188 | fn set_button_state(&mut self, button: Button, state: ButtonState) { 189 | let s = self.0; 190 | 191 | let mask = 1 << (button as usize); 192 | 193 | self.0 = 194 | match state { 195 | ButtonState::Pressed => s & !mask, 196 | ButtonState::Released => s | mask, 197 | }; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/timekeeper.rs: -------------------------------------------------------------------------------- 1 | //! XXX: All of this is very much *not* overflow tolerant. I'm just 2 | //! hoping that a u64 will work for the time being but with the fixed 3 | //! point representations shifting things around it's probably going 4 | //! to be a problem sooner or later. 5 | 6 | use std::{fmt}; 7 | 8 | /// List of all peripherals requiring a TimeSheet. The value of the 9 | /// enum is used as the index in the timesheet table 10 | #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 11 | pub enum Peripheral { 12 | /// Graphics Processing Unit 13 | Gpu, 14 | /// Timers 15 | Timer0, 16 | Timer1, 17 | Timer2, 18 | /// Gamepad/Memory Card controller 19 | PadMemCard, 20 | /// CD-ROM controller 21 | CdRom, 22 | } 23 | 24 | 25 | /// Struct keeping track of the various peripheral's emulation advancement. 26 | #[derive(RustcDecodable, RustcEncodable)] 27 | pub struct TimeKeeper { 28 | /// Counter keeping track of the current date. Unit is a period of 29 | /// the CPU clock at 33.8685MHz (~29.5ns) 30 | now: Cycles, 31 | /// Next time a peripheral needs an update 32 | next_sync: Cycles, 33 | /// Time sheets for keeping track of the various peripherals 34 | timesheets: [TimeSheet; 6], 35 | } 36 | 37 | impl TimeKeeper { 38 | pub fn new() -> TimeKeeper { 39 | TimeKeeper { 40 | now: 0, 41 | // Force a sync at the start to initialize evrything 42 | next_sync: 0, 43 | timesheets: [TimeSheet::new(); 6], 44 | } 45 | } 46 | 47 | pub fn now(&self) -> Cycles { 48 | self.now 49 | } 50 | 51 | pub fn tick(&mut self, cycles: Cycles) { 52 | self.now += cycles; 53 | } 54 | 55 | /// Synchronize the timesheet for the given peripheral and return 56 | /// the elapsed time synce the last sync. 57 | pub fn sync(&mut self, who: Peripheral) -> Cycles { 58 | self.timesheets[who as usize].sync(self.now) 59 | } 60 | 61 | pub fn set_next_sync_delta(&mut self, who: Peripheral, delta: Cycles) { 62 | let date = self.now + delta; 63 | 64 | self.timesheets[who as usize].set_next_sync(date); 65 | 66 | if date < self.next_sync { 67 | self.next_sync = date; 68 | } 69 | } 70 | 71 | /// Set next sync *only* if it's sooner than what's already 72 | /// configured. 73 | pub fn maybe_set_next_sync(&mut self, 74 | who: Peripheral, 75 | date: Cycles) { 76 | let timesheet = &mut self.timesheets[who as usize]; 77 | 78 | let next_sync = timesheet.next_sync(); 79 | 80 | if next_sync > date { 81 | timesheet.set_next_sync(date); 82 | } 83 | } 84 | 85 | /// Set next sync delta *only* if it's sooner than what's already 86 | /// configured. 87 | pub fn maybe_set_next_sync_delta(&mut self, 88 | who: Peripheral, 89 | delta: Cycles) { 90 | let date = self.now + delta; 91 | 92 | self.maybe_set_next_sync(who, date); 93 | } 94 | 95 | /// Called by a peripheral when there's no asynchronous event 96 | /// scheduled. 97 | pub fn no_sync_needed(&mut self, who: Peripheral) { 98 | // Instead of disabling the sync completely we can just use a 99 | // distant date. Peripheral's syncs should be idempotent 100 | // anyway. 101 | self.timesheets[who as usize].set_next_sync(Cycles::max_value()); 102 | } 103 | 104 | pub fn sync_pending(&self) -> bool{ 105 | self.next_sync <= self.now 106 | } 107 | 108 | pub fn needs_sync(&self, who: Peripheral) -> bool { 109 | self.timesheets[who as usize].needs_sync(self.now) 110 | } 111 | 112 | pub fn update_sync_pending(&mut self) { 113 | self.next_sync = 114 | self.timesheets.iter().map(|t| t.next_sync).min().unwrap(); 115 | } 116 | } 117 | 118 | impl fmt::Display for TimeKeeper { 119 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 120 | let now = self.now; 121 | let cpu_freq = ::cpu::CPU_FREQ_HZ as Cycles; 122 | 123 | let seconds = now / cpu_freq; 124 | let rem = now % cpu_freq; 125 | 126 | write!(fmt, "{}s+{:08}", seconds, rem) 127 | } 128 | } 129 | 130 | 131 | #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 132 | /// Struct used to keep track of individual peripherals 133 | struct TimeSheet { 134 | /// Date of the last synchronization 135 | last_sync: Cycles, 136 | /// Date of the next "forced" sync 137 | next_sync: Cycles, 138 | } 139 | 140 | impl TimeSheet { 141 | 142 | fn new() -> TimeSheet { 143 | TimeSheet { 144 | last_sync: 0, 145 | // We force a synchronization at startup to initialize 146 | // everything 147 | next_sync: 0, 148 | } 149 | } 150 | 151 | /// Forward the time sheet to the current date and return the 152 | /// elapsed time since the last sync. 153 | fn sync(&mut self, now: Cycles) -> Cycles { 154 | let delta = now - self.last_sync; 155 | 156 | self.last_sync = now; 157 | 158 | delta 159 | } 160 | 161 | fn next_sync(&self) -> Cycles { 162 | self.next_sync 163 | } 164 | 165 | fn set_next_sync(&mut self, when: Cycles) { 166 | self.next_sync = when; 167 | } 168 | 169 | fn needs_sync(&self, now: Cycles) -> bool { 170 | self.next_sync <= now 171 | } 172 | } 173 | 174 | /// 64bit timestamps will wrap in roughly 17271 years with a CPU clock 175 | /// at 33.8685MHz so it should be plenty enough. 176 | pub type Cycles = u64; 177 | 178 | /// Fixed point representation of a cycle counter used to store 179 | /// non-integer cycle counts. Required because the CPU and GPU clocks 180 | /// have a non-integer ratio. 181 | #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 182 | pub struct FracCycles(Cycles); 183 | 184 | impl FracCycles { 185 | pub fn from_fp(val: Cycles) -> FracCycles { 186 | FracCycles(val) 187 | } 188 | 189 | pub fn from_f32(val: f32) -> FracCycles { 190 | let precision = (1u32 << FracCycles::frac_bits()) as f32; 191 | 192 | FracCycles((val * precision) as Cycles) 193 | } 194 | 195 | pub fn from_cycles(val: Cycles) -> FracCycles { 196 | FracCycles(val << FracCycles::frac_bits()) 197 | } 198 | 199 | /// Return the raw fixed point value 200 | pub fn get_fp(self) -> Cycles { 201 | self.0 202 | } 203 | 204 | /// Return the number of fractional bits in the fixed point 205 | /// representation 206 | pub fn frac_bits() -> Cycles { 207 | 16 208 | } 209 | 210 | pub fn add(self, val: FracCycles) -> FracCycles { 211 | FracCycles(self.get_fp() + val.get_fp()) 212 | } 213 | 214 | pub fn multiply(self, mul: FracCycles) -> FracCycles { 215 | let v = self.get_fp() * mul.get_fp(); 216 | 217 | // The shift amount is doubled during the multiplication so we 218 | // have to shift it back to its normal position. 219 | FracCycles(v >> FracCycles::frac_bits()) 220 | } 221 | 222 | pub fn divide(self, denominator: FracCycles) -> FracCycles { 223 | // In order not to lose precision we must shift the numerator 224 | // once more *before* the division. Otherwise the division of 225 | // the two shifted value would only give us the integer part 226 | // of the result. 227 | let numerator = self.get_fp() << FracCycles::frac_bits(); 228 | 229 | FracCycles(numerator / denominator.get_fp()) 230 | } 231 | 232 | pub fn ceil(self) -> Cycles { 233 | let shift = FracCycles::frac_bits(); 234 | 235 | let align = (1 << shift) - 1; 236 | 237 | (self.0 + align) >> shift 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/mdec/mod.rs: -------------------------------------------------------------------------------- 1 | use memory::Addressable; 2 | use shared::SharedState; 3 | use tracer::module_tracer; 4 | 5 | /// Motion Decoder (sometimes called macroblock or movie decoder). 6 | #[derive(RustcDecodable, RustcEncodable)] 7 | pub struct MDec { 8 | dma_in_enable: bool, 9 | dma_out_enable: bool, 10 | output_depth: OutputDepth, 11 | output_signed: bool, 12 | /// When the output depth is set to 15bpp this variable is used to 13 | /// fill the 16th bit 14 | output_bit15: bool, 15 | current_block: BlockType, 16 | /// Quantization matrices: 8x8 bytes, first one is for luma, 2nd 17 | /// is for chroma. 18 | quant_matrices: [QuantMatrix; 2], 19 | /// Inverse discrete cosine transform matrix. No$ says that "all 20 | /// known games" use the same values for this matrix, so it could 21 | /// be possible to optimize the decoding for this particular 22 | /// table. 23 | idct_matrix: IdctMatrix, 24 | /// Callback handling writes to the command register. Returns 25 | /// `false` when it receives the last word for the command. 26 | command_handler: CommandHandler, 27 | /// Remaining words expected for this command 28 | command_remaining: u16, 29 | } 30 | 31 | impl MDec { 32 | pub fn new() -> MDec { 33 | MDec { 34 | dma_in_enable: false, 35 | dma_out_enable: false, 36 | output_depth: OutputDepth::D4Bpp, 37 | output_signed: false, 38 | output_bit15: false, 39 | current_block: BlockType::CrLuma, 40 | quant_matrices: [QuantMatrix::new(), 41 | QuantMatrix::new()], 42 | idct_matrix: IdctMatrix::new(), 43 | command_handler: CommandHandler(MDec::handle_command), 44 | command_remaining: 1, 45 | } 46 | } 47 | 48 | pub fn load(&mut self, 49 | _: &mut SharedState, 50 | offset: u32) -> u32 { 51 | 52 | if A::size() != 4 { 53 | panic!("Unhandled MDEC load ({})", A::size()); 54 | } 55 | 56 | match offset { 57 | 4 => self.status(), 58 | _ => panic!("Unhandled MDEC load: {:08x}", offset), 59 | } 60 | } 61 | 62 | 63 | pub fn store(&mut self, 64 | shared: &mut SharedState, 65 | offset: u32, 66 | val: u32) { 67 | 68 | if A::size() != 4 { 69 | panic!("Unhandled MDEC store ({})", A::size()); 70 | } 71 | 72 | match offset { 73 | 0 => self.command(shared, val), 74 | 4 => self.set_control(val), 75 | _ => panic!("Unhandled MDEC store: {:08x} {:08x}", offset, val), 76 | } 77 | } 78 | 79 | /// Status register 80 | pub fn status(&self) -> u32 { 81 | let mut r = 0; 82 | 83 | // Bits [15:0] contain the number of remaining parameter words 84 | // minus 1, or 0xffff if no parameter is expected. 85 | r |= self.command_remaining.wrapping_sub(1) as u32; 86 | 87 | // XXX Implement bits [18:16]: current block 88 | 89 | r |= (self.output_bit15 as u32) << 23; 90 | r |= (self.output_signed as u32) << 24; 91 | r |= (self.output_depth as u32) << 25; 92 | 93 | // XXX Implement bits 27 and 28: DMA data in/out request 94 | 95 | // Command busy flag. XXX Probably set for a little while 96 | // after the last parameter is received whilet he command is 97 | // being processed? 98 | let command_pending = 99 | *self.command_handler as usize != MDec::handle_command as usize; 100 | 101 | r |= (command_pending as u32) << 29; 102 | 103 | // XXX Implement bit 30: data in FIFO full 104 | r |= 0 << 30; 105 | // XXX Implement bit 31: data out FIFO empty 106 | r |= 1 << 31; 107 | 108 | r 109 | } 110 | 111 | /// Handle writes to the command register 112 | pub fn command(&mut self, shared: &mut SharedState, cmd: u32) { 113 | 114 | module_tracer("MDEC", |m| { 115 | m.trace(shared.tk().now(), 116 | "command_word", 117 | cmd); 118 | }); 119 | 120 | self.command_remaining -= 1; 121 | 122 | (self.command_handler)(self, cmd); 123 | 124 | if self.command_remaining == 0 { 125 | *self.command_handler = MDec::handle_command; 126 | self.command_remaining = 1; 127 | } 128 | } 129 | 130 | fn handle_command(&mut self, cmd: u32) { 131 | let opcode = cmd >> 29; 132 | 133 | // Those internal variables (accessible through the status 134 | // register) are updated no matter the opcode. They're 135 | // probably meaningless for table loading opcodes though (most 136 | // likely full 0s). 137 | self.output_depth = 138 | match (cmd >> 27) & 3 { 139 | 0 => OutputDepth::D4Bpp, 140 | 1 => OutputDepth::D8Bpp, 141 | 2 => OutputDepth::D24Bpp, 142 | 3 => OutputDepth::D15Bpp, 143 | _ => unreachable!(), 144 | }; 145 | self.output_signed = (cmd >> 26) & 1 != 0; 146 | self.output_bit15 = (cmd >> 25) & 1 != 0; 147 | 148 | let (len, handler): (u16, fn(&mut MDec, u32)) = 149 | match opcode { 150 | // Set quantization matrices. Bit 0 tells us whether we're 151 | // setting only the luma table or luma + chroma. 152 | 2 => match cmd & 1 != 0 { 153 | true => (32, MDec::handle_color_quant_matrices), 154 | false => (16, MDec::handle_monochrome_quant_matrix), 155 | }, 156 | 3 => (32, MDec::handle_idct_matrix), 157 | n => { 158 | warn!("Unsupported MDEC opcode {} ({:08x})", n, cmd); 159 | (1, MDec::handle_command) 160 | } 161 | }; 162 | 163 | self.command_remaining = len; 164 | *self.command_handler = handler; 165 | } 166 | 167 | fn handle_color_quant_matrices(&mut self, cmd: u32) { 168 | let index = (31 - self.command_remaining) as usize; 169 | 170 | let matrix = index / 16; 171 | let index = (index % 16) * 4; 172 | 173 | for i in 0..4 { 174 | let b = (cmd >> (i * 8)) as u8; 175 | 176 | self.quant_matrices[matrix][index + i] = b; 177 | } 178 | } 179 | 180 | fn handle_monochrome_quant_matrix(&mut self, cmd: u32) { 181 | let index = (15 - self.command_remaining) as usize; 182 | 183 | let index = index * 4; 184 | 185 | for i in 0..4 { 186 | let b = (cmd >> (i * 8)) as u8; 187 | 188 | self.quant_matrices[0][index + i] = b; 189 | } 190 | } 191 | 192 | fn handle_idct_matrix(&mut self, cmd: u32) { 193 | let index = (31 - self.command_remaining) as usize; 194 | 195 | let index = index * 2; 196 | 197 | self.idct_matrix[index] = cmd as i16; 198 | self.idct_matrix[index + 1] = (cmd >> 16) as i16; 199 | } 200 | 201 | /// Set the value of the control register 202 | fn set_control(&mut self, val: u32) { 203 | let reset = val & (1 << 31) != 0; 204 | 205 | self.dma_in_enable = val & (1 << 30) != 0; 206 | self.dma_out_enable = val & (1 << 29) != 0; 207 | 208 | if reset { 209 | // XXX Does this reset anything else? DMA IN/DMA OUT 210 | // flags for instance? How about the various tables? 211 | 212 | // XXX clear FIFOs 213 | self.output_depth = OutputDepth::D4Bpp; 214 | self.output_signed = false; 215 | self.output_bit15 = false; 216 | self.current_block = BlockType::CrLuma; 217 | *self.command_handler = MDec::handle_command; 218 | self.command_remaining = 1; 219 | } 220 | } 221 | } 222 | 223 | callback!(struct CommandHandler (fn(&mut MDec, u32)) { 224 | MDec::handle_command, 225 | MDec::handle_color_quant_matrices, 226 | MDec::handle_monochrome_quant_matrix, 227 | }); 228 | 229 | /// Serializable container for the quantization matrices 230 | buffer!(struct QuantMatrix([u8; 64])); 231 | 232 | /// Serializable container for the IDCT matrix 233 | buffer!(struct IdctMatrix([i16; 64])); 234 | 235 | /// Pixel color depths supported by the MDEC 236 | #[derive(Copy, Clone, PartialEq, Eq, Debug, RustcDecodable, RustcEncodable)] 237 | enum OutputDepth { 238 | D4Bpp = 0, 239 | D8Bpp = 1, 240 | D15Bpp = 3, 241 | D24Bpp = 2, 242 | } 243 | 244 | #[allow(dead_code)] 245 | #[derive(RustcDecodable, RustcEncodable)] 246 | enum BlockType { 247 | Y1 = 0, 248 | Y2 = 1, 249 | Y3 = 2, 250 | Y4 = 3, 251 | /// Luma (Y) for monochrome, Cr otherwise 252 | CrLuma = 4, 253 | Cb = 5, 254 | } 255 | -------------------------------------------------------------------------------- /src/cdrom/iso9660.rs: -------------------------------------------------------------------------------- 1 | use cdimage::{Image, CdError}; 2 | use cdimage::sector::Sector; 3 | use cdimage::msf::Msf; 4 | use cdimage::bcd::Bcd; 5 | 6 | /// Structure representing an ISO9660 directory 7 | pub struct Directory { 8 | /// Contents of the directory 9 | entries: Vec, 10 | } 11 | 12 | impl Directory { 13 | pub fn new(image: &mut Image, entry: &Entry) -> Result { 14 | 15 | if !entry.is_dir() { 16 | return Err(Error::NotADirectory); 17 | } 18 | 19 | let mut dir = 20 | Directory { 21 | entries: Vec::new(), 22 | }; 23 | 24 | // Directory entries cannot span multiple sectors so it's safe 25 | // to handle them one by one 26 | let mut extent_len = entry.extent_len() as usize; 27 | let extent_location = entry.extent_location(); 28 | 29 | let track_msf = 30 | match Msf::from_sector_index(extent_location) { 31 | Some(m) => m, 32 | None => return Err(Error::BadExtent(extent_location)), 33 | }; 34 | 35 | let mut msf = try!(image.track_msf(Bcd::one(), track_msf)); 36 | 37 | let mut sector = Sector::empty(); 38 | 39 | while extent_len > 0 { 40 | try!(image.read_sector(&mut sector, msf)); 41 | 42 | let data = try!(sector.mode2_xa_payload()); 43 | 44 | let len = 45 | if extent_len > 2048 { 46 | 2048 47 | } else { 48 | extent_len 49 | }; 50 | 51 | try!(dir.parse_entries(&data[0..len])); 52 | 53 | extent_len -= len; 54 | msf = msf.next().unwrap(); 55 | } 56 | 57 | Ok(dir) 58 | } 59 | 60 | fn parse_entries(&mut self, mut raw: &[u8]) -> Result<(), Error> { 61 | 62 | while raw.len() > 0 { 63 | let dir_len = raw[0] as usize; 64 | 65 | if dir_len == 0 { 66 | // It seems we've reached the last directory. Or at 67 | // least I think so? I'm not entirely sure how 68 | // directories which span several sectors are handled, 69 | // if the padding is not part of any entry then we 70 | // should skip ahead to the next sector. Needs more 71 | // testing. 72 | break; 73 | } 74 | 75 | if dir_len < 34 { 76 | let desc = format!("Directory entry too short ({})", dir_len); 77 | return Err(Error::BadFormat(desc)); 78 | } 79 | 80 | let name_len = raw[32] as usize; 81 | 82 | let name_end = 33 + name_len; 83 | 84 | if name_end > dir_len { 85 | return Err(Error::BadFormat("Entry name too long".into())); 86 | } 87 | 88 | self.entries.push(Entry::new(&raw[0..dir_len])); 89 | 90 | raw = &raw[dir_len..]; 91 | } 92 | 93 | Ok(()) 94 | } 95 | 96 | /// Attempt to "cd" to a subdirectory, returning a new `Directory` 97 | /// instance 98 | pub fn cd(&self, 99 | image: &mut Image, 100 | name: &[u8]) -> Result { 101 | let entry = try!(self.entry_by_name(name)); 102 | 103 | Directory::new(image, entry) 104 | } 105 | 106 | pub fn entry_by_name(&self, name: &[u8]) -> Result<&Entry, Error> { 107 | match 108 | self.entries.iter().find(|e| e.name() == name) { 109 | Some(e) => Ok(e), 110 | None => Err(Error::EntryNotFound), 111 | } 112 | } 113 | 114 | /// Retreive a list of all the entries in this directory 115 | pub fn ls(&self) -> &[Entry] { 116 | &self.entries 117 | } 118 | } 119 | 120 | /// A single directory entry 121 | pub struct Entry(Vec); 122 | 123 | impl Entry { 124 | 125 | fn new(entry: &[u8]) -> Entry { 126 | Entry(entry.into()) 127 | } 128 | 129 | pub fn name(&self) -> &[u8] { 130 | let name_len = self.0[32] as usize; 131 | 132 | let name_end = 33 + name_len; 133 | 134 | // No need to validate the len, it should've been done on 135 | // entry creation 136 | &self.0[33..name_end] 137 | } 138 | 139 | pub fn is_dir(&self) -> bool { 140 | let flags = self.0[25]; 141 | 142 | (flags & 0x2) != 0 143 | } 144 | 145 | pub fn extent_location(&self) -> u32 { 146 | read_u32(&self.0[2..10]) 147 | } 148 | 149 | pub fn extent_len(&self) -> u32 { 150 | read_u32(&self.0[10..18]) 151 | } 152 | 153 | pub fn read_file(&self, image: &mut Image) -> Result, Error> { 154 | if self.is_dir() { 155 | return Err(Error::NotAFile); 156 | } 157 | 158 | let mut extent_len = self.extent_len() as usize; 159 | let extent_location = self.extent_location(); 160 | 161 | let mut contents = Vec::with_capacity(extent_len); 162 | 163 | let track_msf = 164 | match Msf::from_sector_index(extent_location) { 165 | Some(m) => m, 166 | None => return Err(Error::BadExtent(extent_location)), 167 | }; 168 | 169 | let mut msf = try!(image.track_msf(Bcd::one(), track_msf)); 170 | 171 | let mut sector = Sector::empty(); 172 | 173 | while extent_len > 0 { 174 | try!(image.read_sector(&mut sector, msf)); 175 | 176 | let data = try!(sector.mode2_xa_payload()); 177 | 178 | let len = 179 | if extent_len > 2048 { 180 | 2048 181 | } else { 182 | extent_len 183 | }; 184 | 185 | for &b in &data[0..len] { 186 | contents.push(b); 187 | } 188 | 189 | extent_len -= len; 190 | msf = msf.next().unwrap(); 191 | } 192 | 193 | Ok(contents) 194 | } 195 | } 196 | 197 | #[derive(Debug)] 198 | pub enum Error { 199 | /// Cdimage access error 200 | CdError(CdError), 201 | /// Couldn't find the ISO9660 magic "CD0001" 202 | BadMagic, 203 | /// Couldn't find the Primary Volume Descriptor 204 | MissingPrimaryVolumeDescriptor, 205 | /// Unexpected Volume Descriptor version 206 | BadVolumDescriptorVersion, 207 | /// Encountered an invalid extent location 208 | BadExtent(u32), 209 | /// Miscellaneous ISO9660 format error containing a description of 210 | /// the problem 211 | BadFormat(String), 212 | /// The requested entry couldn't be found 213 | EntryNotFound, 214 | /// We expected a directory and got a file 215 | NotADirectory, 216 | /// We expected a file and got a directory 217 | NotAFile, 218 | } 219 | 220 | impl From for Error { 221 | fn from(e: CdError) -> Error { 222 | Error::CdError(e) 223 | } 224 | } 225 | 226 | pub fn open_image(image: &mut Image) -> Result { 227 | // The first 16 sectors are the "system area" which is ignored by 228 | // the ISO filesystem. The Volume Descriptor Set should start at 229 | // 00:00:16 in track 01 230 | let mut msf = try!(image.track_msf(Bcd::one(), 231 | Msf::from_bcd(0, 0, 0x16).unwrap())); 232 | 233 | let mut sector = Sector::empty(); 234 | 235 | // Look for the primary volume descriptor 236 | loop { 237 | try!(image.read_sector(&mut sector, msf)); 238 | 239 | let volume_descriptor = try!(sector.mode2_xa_payload()); 240 | 241 | // Check volume descriptor "standard identifier" 242 | if &volume_descriptor[1..6] != b"CD001" { 243 | return Err(Error::BadMagic); 244 | } 245 | 246 | // Byte 0 contains the "volume descriptor type". 247 | match volume_descriptor[0] { 248 | // Primary Volume Descriptor 249 | 0x01 => break, 250 | // Volume Descriptor Set Terminator 251 | 0xff => return Err(Error::MissingPrimaryVolumeDescriptor), 252 | // Unhandled volume descriptor type, ignore 253 | _ => (), 254 | } 255 | 256 | // Not the primary volume descriptor, move on to the next 257 | // sector 258 | msf = msf.next().unwrap(); 259 | } 260 | 261 | let volume_descriptor = try!(sector.mode2_xa_payload()); 262 | 263 | // Volume Descriptor Version 264 | if volume_descriptor[6] != 0x01 { 265 | return Err(Error::BadVolumDescriptorVersion); 266 | } 267 | 268 | // We can now open the root directory descriptor 269 | let root_dir_descriptor = &volume_descriptor[156..190]; 270 | 271 | let root_dir = Entry::new(root_dir_descriptor); 272 | Directory::new(image, &root_dir) 273 | } 274 | 275 | /// Read a 32bit number stored in "both byte order" format 276 | fn read_u32(v: &[u8]) -> u32 { 277 | // Only use the little endian representation. Should we bother 278 | // validating that the BE version is coherent? 279 | v[0] as u32 | 280 | ((v[1] as u32) << 8) | 281 | ((v[2] as u32) << 16) | 282 | ((v[3] as u32) << 24) 283 | } 284 | -------------------------------------------------------------------------------- /src/cdrom/disc.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use cdimage::{Image, CdError}; 4 | use cdimage::msf::Msf; 5 | use cdimage::bcd::Bcd; 6 | use cdimage::sector::Sector; 7 | 8 | use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; 9 | 10 | use super::iso9660; 11 | 12 | /// PlayStation disc. 13 | /// 14 | /// XXX: add support for CD-DA? Not really useful but shouldn't 15 | /// be very hard either. We need to support audio tracks anyway... 16 | pub struct Disc { 17 | /// Image file 18 | image: Box, 19 | /// Disc serial number 20 | serial: SerialNumber, 21 | } 22 | 23 | impl Disc { 24 | /// Reify a disc using `image` as a backend. 25 | pub fn new(mut image: Box) -> Result { 26 | let serial = 27 | match extract_serial_number(&mut *image) { 28 | Some(s) => s, 29 | None => { 30 | return Err("Couldn't find disc serial number".into()); 31 | } 32 | }; 33 | 34 | let disc = Disc { 35 | image: image, 36 | serial: serial, 37 | }; 38 | 39 | Ok(disc) 40 | } 41 | 42 | pub fn region(&self) -> Region { 43 | // For now I prefer to panic to catch potential issues with 44 | // the serial number handling code, alternatively we could 45 | // fallback on `extract_system_region` 46 | match self.serial.region() { 47 | Some(r) => r, 48 | None => panic!("Can't establish the region of {}", self.serial), 49 | } 50 | } 51 | 52 | pub fn serial_number(&self) -> SerialNumber { 53 | self.serial 54 | } 55 | 56 | pub fn image(&mut self) -> &mut Image { 57 | &mut*self.image 58 | } 59 | } 60 | 61 | impl Encodable for Disc { 62 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 63 | // Only encode the serial number 64 | self.serial.encode(s) 65 | } 66 | } 67 | 68 | impl Decodable for Disc { 69 | fn decode(d: &mut D) -> Result { 70 | let serial = try!(SerialNumber::decode(d)); 71 | 72 | // Placeholder disc image 73 | Ok(Disc { 74 | image: Box::new(MissingImage), 75 | serial: serial, 76 | }) 77 | } 78 | } 79 | 80 | /// Dummy Image implemementation used when deserializing a Disc. Since 81 | /// we don't want to store the entire disc in the image it will be 82 | /// missing after a load, it's up to the frontend to make sure to 83 | /// reload the image. 84 | struct MissingImage; 85 | 86 | impl Image for MissingImage { 87 | fn image_format(&self) -> String { 88 | panic!("Missing CD image!"); 89 | } 90 | 91 | fn read_sector(&mut self, _: &mut Sector, _: Msf) -> Result<(), CdError> { 92 | panic!("Missing CD image!"); 93 | } 94 | 95 | fn track_msf(&self, _: Bcd, _: Msf) -> Result { 96 | panic!("Missing CD image!"); 97 | } 98 | } 99 | 100 | /// Disc region 101 | #[derive(Clone, Copy, Debug, PartialEq, Eq, RustcDecodable, RustcEncodable)] 102 | pub enum Region { 103 | /// Japan (NTSC): SCEI 104 | Japan, 105 | /// North America (NTSC): SCEA 106 | NorthAmerica, 107 | /// Europe (PAL): SCEE 108 | Europe, 109 | } 110 | 111 | /// Disc serial number 112 | #[derive(Copy, Clone, PartialEq, Eq, RustcDecodable, RustcEncodable)] 113 | pub struct SerialNumber([u8; 10]); 114 | 115 | impl SerialNumber { 116 | /// Create a dummy serial number: UNKN-00000. Used when no serial 117 | /// number can be found. 118 | pub fn dummy() -> SerialNumber { 119 | SerialNumber(*b"UNKN-00000") 120 | } 121 | 122 | /// Extract a serial number from a standard PlayStation binary 123 | /// name of the form "aaaa_ddd.dd" 124 | fn from_bin_name(bin: &[u8]) -> Option { 125 | if bin.len() != 11 { 126 | return None; 127 | } 128 | 129 | if bin[4] != b'_' { 130 | // This will fail for the few "lightspan educational" 131 | // discs since they have a serial number looking like 132 | // "LSP-123456". Those games are fairly obscure and 133 | // some of them seem to have weird and nonstandards 134 | // SYSTEM.CNF anyway. 135 | return None; 136 | } 137 | 138 | let mut serial = [0u8; 10]; 139 | 140 | let to_upper = |b| { 141 | if b >= b'a' && b <= b'z' { 142 | b - b'a' + b'A' 143 | } else { 144 | b 145 | } 146 | }; 147 | 148 | serial[0] = to_upper(bin[0]); 149 | serial[1] = to_upper(bin[1]); 150 | serial[2] = to_upper(bin[2]); 151 | serial[3] = to_upper(bin[3]); 152 | serial[4] = b'-'; 153 | serial[5] = bin[5]; 154 | serial[6] = bin[6]; 155 | serial[7] = bin[7]; 156 | serial[8] = bin[9]; 157 | serial[9] = bin[10]; 158 | 159 | Some(SerialNumber(serial)) 160 | } 161 | 162 | pub fn region(&self) -> Option { 163 | match &self.0[0..4] { 164 | b"SCPS" | b"SLPS" | b"SLPM" | b"PAPX" => Some(Region::Japan), 165 | b"SCUS" | b"SLUS" | b"LSP-" => Some(Region::NorthAmerica), 166 | b"SCES" | b"SCED" | b"SLES" | b"SLED" => Some(Region::Europe), 167 | _ => None, 168 | } 169 | } 170 | } 171 | 172 | impl fmt::Display for SerialNumber { 173 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 174 | write!(f, "{}", String::from_utf8_lossy(&self.0)) 175 | } 176 | } 177 | 178 | /// Attempt to discover the region of the disc using the license 179 | /// string stored in the system area of the official PlayStation 180 | /// ISO filesystem. 181 | pub fn extract_system_region(image: &mut Image) -> Result { 182 | // In order to identify the type of disc we're going to use 183 | // sector 00:00:04 from Track01 which should contain the 184 | // "Licensed by..." string. 185 | let msf = try!(image.track_msf(Bcd::one(), 186 | Msf::from_bcd(0, 0, 4).unwrap())); 187 | 188 | let mut sector = Sector::empty(); 189 | 190 | try!(image.read_sector(&mut sector, msf)); 191 | 192 | // On the discs I've tried we always have an ASCII license 193 | // string in the first 76 data bytes. We'll see if it holds 194 | // true for all the discs out there... 195 | let license_blob = &try!(sector.mode2_xa_payload())[0..76]; 196 | 197 | // There are spaces everywhere in the license string 198 | // (including in the middle of some words), let's clean it up 199 | // and convert to a canonical string 200 | let license: String = license_blob.iter() 201 | .filter_map(|&b| { 202 | match b { 203 | b'A'...b'z' => Some(b as char), 204 | _ => None, 205 | } 206 | }) 207 | .collect(); 208 | 209 | let region = 210 | match license.as_ref() { 211 | "LicensedbySonyComputerEntertainmentInc" 212 | => Region::Japan, 213 | "LicensedbySonyComputerEntertainmentAmerica" 214 | => Region::NorthAmerica, 215 | "LicensedbySonyComputerEntertainmentofAmerica" 216 | => Region::NorthAmerica, 217 | "LicensedbySonyComputerEntertainmentEurope" 218 | => Region::Europe, 219 | _ => { 220 | warn!("Couldn't identify disc region string: {}", license); 221 | return Err(CdError::BadFormat); 222 | } 223 | }; 224 | 225 | Ok(region) 226 | } 227 | 228 | /// Attempt to extract the serial number of the disc. All officially 229 | /// licensed PlayStation game should have a serial number. 230 | fn extract_serial_number(image: &mut Image) -> Option { 231 | 232 | let system_cnf = 233 | match read_system_cnf(image) { 234 | Ok(c) => c, 235 | Err(e) => { 236 | warn!("Couldn't read SYSTEM.CNF: {:?}", e); 237 | return None; 238 | } 239 | }; 240 | 241 | // Now we need to parse the SYSTEM.CNF file to get the content of 242 | // the "BOOT" line 243 | let mut boot_path = None; 244 | 245 | for line in system_cnf.split(|&b| b == b'\n') { 246 | let words: Vec<_> = line 247 | .split(|&b| b == b' ' || b == b'\t' || b == b'=') 248 | .filter(|w| !w.is_empty()) 249 | .collect(); 250 | 251 | if words.len() == 2 { 252 | if words[0] == b"BOOT" { 253 | boot_path = Some(words[1]); 254 | break; 255 | } 256 | } 257 | } 258 | 259 | let boot_path = 260 | match boot_path { 261 | Some(b) => b, 262 | None => { 263 | warn!("Couldn't find BOOT line in SYSTEM.CNF"); 264 | return None; 265 | } 266 | }; 267 | 268 | // boot_path should look like "cdrom:\FOO\BAR\...\aaaa_ddd.dd;1" 269 | let path: Vec<_> = boot_path 270 | .split(|&b| b == b':' || b == b';' || b == b'\\') 271 | .collect(); 272 | 273 | if path.len() < 2 { 274 | warn!("Unexpected boot path: {}", String::from_utf8_lossy(boot_path)); 275 | return None; 276 | } 277 | 278 | let bin_name = path[path.len() - 2]; 279 | 280 | let serial = SerialNumber::from_bin_name(&bin_name); 281 | 282 | if serial.is_none() { 283 | warn!("Unexpected bin name: {}", String::from_utf8_lossy(bin_name)); 284 | } 285 | 286 | serial 287 | } 288 | 289 | fn read_system_cnf(image: &mut Image) -> Result, iso9660::Error> { 290 | let dir = try!(iso9660::open_image(image)); 291 | 292 | let system_cnf = try!(dir.entry_by_name(b"SYSTEM.CNF;1")); 293 | 294 | // SYSTEM.CNF should be a small text file, 1MB should bb way more 295 | // than necessary 296 | let len = system_cnf.extent_len(); 297 | 298 | if len > 1024 * 1024 { 299 | let desc = format!("SYSTEM.CNF is too big: {}B", len); 300 | return Err(iso9660::Error::BadFormat(desc)); 301 | } 302 | 303 | system_cnf.read_file(image) 304 | } 305 | -------------------------------------------------------------------------------- /src/memory/dma.rs: -------------------------------------------------------------------------------- 1 | use shared::SharedState; 2 | use interrupt::Interrupt; 3 | 4 | use tracer::SizedValue; 5 | 6 | /// Direct Memory Access 7 | #[derive(RustcDecodable, RustcEncodable)] 8 | pub struct Dma { 9 | /// DMA control register 10 | control: u32, 11 | /// master IRQ enable 12 | irq_en: bool, 13 | /// IRQ enable for individual channels 14 | channel_irq_en: u8, 15 | /// IRQ flags for individual channels 16 | channel_irq_flags: u8, 17 | /// When set the interrupt is active unconditionally (even if 18 | /// `irq_en` is false) 19 | force_irq: bool, 20 | /// Bits [0:5] of the interrupt registers are RW but I don't know 21 | /// what they're supposed to do so I just store them and send them 22 | /// back untouched on reads 23 | irq_dummy: u8, 24 | /// The 7 channel instances 25 | channels: [Channel; 7], 26 | } 27 | 28 | impl Dma { 29 | pub fn new() -> Dma { 30 | Dma { 31 | // Reset value taken from the Nocash PSX spec 32 | control: 0x07654321, 33 | irq_en: false, 34 | channel_irq_en: 0, 35 | channel_irq_flags: 0, 36 | force_irq: false, 37 | irq_dummy: 0, 38 | channels: [ Channel::new(); 7 ], 39 | } 40 | } 41 | 42 | /// Return the status of the DMA interrupt 43 | fn irq(&self) -> bool { 44 | let channel_irq = self.channel_irq_flags & self.channel_irq_en; 45 | 46 | self.force_irq || (self.irq_en && channel_irq != 0) 47 | } 48 | 49 | /// Retrieve the value of the control register 50 | pub fn control(&self) -> u32 { 51 | self.control 52 | } 53 | 54 | /// Set the value of the control register 55 | pub fn set_control(&mut self, val: u32) { 56 | self.control = val 57 | } 58 | 59 | /// Retrieve the value of the interrupt register 60 | pub fn interrupt(&self) -> u32 { 61 | let mut r = 0; 62 | 63 | r |= self.irq_dummy as u32; 64 | r |= (self.force_irq as u32) << 15; 65 | r |= (self.channel_irq_en as u32) << 16; 66 | r |= (self.irq_en as u32) << 23; 67 | r |= (self.channel_irq_flags as u32) << 24; 68 | r |= (self.irq() as u32) << 31; 69 | 70 | r 71 | } 72 | 73 | /// Set the value of the interrupt register 74 | pub fn set_interrupt(&mut self, 75 | shared: &mut SharedState, 76 | val: u32) { 77 | let prev_irq = self.irq(); 78 | 79 | // Unknown what bits [5:0] do 80 | self.irq_dummy = (val & 0x3f) as u8; 81 | 82 | self.force_irq = (val >> 15) & 1 != 0; 83 | 84 | // XXX I don't think disabling the channel IRQ clears the 85 | // interrupt in channel_irq_flags but I should check that. 86 | self.channel_irq_en = ((val >> 16) & 0x7f) as u8; 87 | 88 | self.irq_en = (val >> 23) & 1 != 0; 89 | 90 | // Writing 1 to a flag resets it 91 | let ack = ((val >> 24) & 0x3f) as u8; 92 | self.channel_irq_flags &= !ack; 93 | 94 | if !prev_irq && self.irq() { 95 | // Rising edge of the done interrupt 96 | shared.irq_state_mut().assert(Interrupt::Dma); 97 | } 98 | } 99 | 100 | /// Return a reference to a channel by port number. 101 | pub fn channel(&self, port: Port) -> &Channel { 102 | &self.channels[port as usize] 103 | } 104 | 105 | /// Return a mutable reference to a channel by port number. 106 | pub fn channel_mut(&mut self, port: Port) -> &mut Channel { 107 | &mut self.channels[port as usize] 108 | } 109 | 110 | pub fn done(&mut self, 111 | shared: &mut SharedState, 112 | port: Port) { 113 | 114 | self.channel_mut(port).done(); 115 | 116 | let prev_irq = self.irq(); 117 | 118 | // Set interrupt flag if the channel's interrupt is enabled 119 | let it_en = self.channel_irq_en & (1 << (port as usize)); 120 | 121 | self.channel_irq_flags |= it_en; 122 | 123 | if !prev_irq && self.irq() { 124 | // Rising edge of the done interrupt 125 | shared.irq_state_mut().assert(Interrupt::Dma); 126 | } 127 | } 128 | } 129 | 130 | /// Per-channel data 131 | #[derive(Clone, Copy, RustcDecodable, RustcEncodable)] 132 | pub struct Channel { 133 | /// If true the channel is enabled and the copy can take place 134 | /// depending on the condition mandated by the `sync` mode. 135 | enable: bool, 136 | /// Copy direction: from RAM or from device 137 | direction: Direction, 138 | /// DMA can either increment or decrement the RAM pointer after 139 | /// each copy 140 | step: Step, 141 | /// Synchronization mode 142 | sync: Sync, 143 | /// Used to start the DMA transfer when `sync` is `Manual` 144 | trigger: bool, 145 | /// If true the DMA "chops" the transfer and lets the CPU run in 146 | /// the gaps. 147 | chop: bool, 148 | /// Chopping DMA window size (log2 number of words) 149 | chop_dma_sz: u8, 150 | /// Chopping CPU window size (log2 number of cycles) 151 | chop_cpu_sz: u8, 152 | /// DMA start address 153 | base: u32, 154 | /// Size of a block in words 155 | block_size: u16, 156 | /// Block count, only used when `sync` is `Request` 157 | block_count: u16, 158 | /// Unkown 2 RW bits in configuration register 159 | dummy: u8, 160 | } 161 | 162 | impl Channel { 163 | fn new() -> Channel { 164 | Channel { 165 | enable: false, 166 | direction: Direction::ToRam, 167 | step: Step::Increment, 168 | sync: Sync::Manual, 169 | trigger: false, 170 | chop: false, 171 | chop_dma_sz: 0, 172 | chop_cpu_sz: 0, 173 | base: 0, 174 | block_size: 0, 175 | block_count: 0, 176 | dummy: 0, 177 | } 178 | } 179 | 180 | /// Retrieve the channel's base address 181 | pub fn base(&self) -> u32 { 182 | self.base 183 | } 184 | 185 | /// Set channel base address. Only bits [0:23] are significant so 186 | /// only 16MB are addressable by the DMA 187 | pub fn set_base(&mut self, val: u32) { 188 | self.base = val & 0xffffff; 189 | } 190 | 191 | /// Retrieve the value of the control register 192 | pub fn control(&self) -> u32 { 193 | let mut r = 0; 194 | 195 | r |= (self.direction as u32) << 0; 196 | r |= (self.step as u32) << 1; 197 | r |= (self.chop as u32) << 8; 198 | r |= (self.sync as u32) << 9; 199 | r |= (self.chop_dma_sz as u32) << 16; 200 | r |= (self.chop_cpu_sz as u32) << 20; 201 | r |= (self.enable as u32) << 24; 202 | r |= (self.trigger as u32) << 28; 203 | r |= (self.dummy as u32) << 29; 204 | 205 | r 206 | } 207 | 208 | /// Set the value of the control register 209 | pub fn set_control(&mut self, val: u32) { 210 | 211 | self.direction = match val & 1 != 0 { 212 | true => Direction::FromRam, 213 | false => Direction::ToRam, 214 | }; 215 | 216 | self.step = match (val >> 1) & 1 != 0 { 217 | true => Step::Decrement, 218 | false => Step::Increment, 219 | }; 220 | 221 | self.chop = (val >> 8) & 1 != 0; 222 | 223 | self.sync = match (val >> 9) & 3 { 224 | 0 => Sync::Manual, 225 | 1 => Sync::Request, 226 | 2 => Sync::LinkedList, 227 | n => panic!("Unknown DMA sync mode {}", n), 228 | }; 229 | 230 | self.chop_dma_sz = ((val >> 16) & 7) as u8; 231 | self.chop_cpu_sz = ((val >> 20) & 7) as u8; 232 | 233 | self.enable = (val >> 24) & 1 != 0; 234 | self.trigger = (val >> 28) & 1 != 0; 235 | 236 | self.dummy = ((val >> 29) & 3) as u8; 237 | } 238 | 239 | /// Retrieve value of the Block Control register 240 | pub fn block_control(&self) -> u32 { 241 | let bs = self.block_size as u32; 242 | let bc = self.block_count as u32; 243 | 244 | (bc << 16) | bs 245 | } 246 | 247 | /// Set value of the Block Control register 248 | pub fn set_block_control(&mut self, val: u32) { 249 | self.block_size = val as u16; 250 | self.block_count = (val >> 16) as u16; 251 | } 252 | 253 | /// Return true if the channel has been started 254 | pub fn active(&self) -> bool { 255 | // In manual sync mode the CPU must set the "trigger" bit to 256 | // start the transfer. 257 | let trigger = match self.sync { 258 | Sync::Manual => self.trigger, 259 | _ => true, 260 | }; 261 | 262 | self.enable && trigger 263 | } 264 | 265 | /// Set the channel status to "completed" state 266 | fn done(&mut self) { 267 | self.enable = false; 268 | self.trigger = false; 269 | } 270 | 271 | pub fn direction(&self) -> Direction { 272 | self.direction 273 | } 274 | 275 | pub fn step(&self) -> Step { 276 | self.step 277 | } 278 | 279 | pub fn sync(&self) -> Sync { 280 | self.sync 281 | } 282 | 283 | /// Return the DMA transfer size in bytes or None for linked list 284 | /// mode. 285 | pub fn transfer_size(&self) -> Option { 286 | let bs = self.block_size as u32; 287 | let bc = self.block_count as u32; 288 | 289 | match self.sync { 290 | // For manual mode only the block size is used 291 | Sync::Manual => Some(bs), 292 | // In DMA request mode we must transfer `bc` blocks 293 | Sync::Request => Some(bc * bs), 294 | // In linked list mode the size is not known ahead of 295 | // time: we stop when we encounter the "end of list" 296 | // marker (0xffffff) 297 | Sync::LinkedList => None, 298 | } 299 | } 300 | } 301 | 302 | /// DMA transfer direction 303 | #[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] 304 | pub enum Direction { 305 | ToRam = 0, 306 | FromRam = 1, 307 | } 308 | 309 | /// DMA transfer step 310 | #[derive(Clone, Copy, RustcDecodable, RustcEncodable)] 311 | pub enum Step { 312 | Increment = 0, 313 | Decrement = 1, 314 | } 315 | 316 | /// DMA transfer synchronization mode 317 | #[derive(Clone, Copy, RustcDecodable, RustcEncodable)] 318 | pub enum Sync { 319 | /// Transfer starts when the CPU writes to the Trigger bit and 320 | /// transfers everything at once 321 | Manual = 0, 322 | /// Sync blocks to DMA requests 323 | Request = 1, 324 | /// Used to transfer GPU command lists 325 | LinkedList = 2, 326 | } 327 | 328 | impl From for SizedValue { 329 | fn from(v: Sync) -> SizedValue { 330 | SizedValue(v as u32, 2) 331 | } 332 | } 333 | 334 | /// The 7 DMA ports 335 | #[derive(Clone, Copy, PartialEq, Eq, Debug, RustcDecodable, RustcEncodable)] 336 | pub enum Port { 337 | /// Macroblock decoder input 338 | MDecIn = 0, 339 | /// Macroblock decoder output 340 | MDecOut = 1, 341 | /// Graphics Processing Unit 342 | Gpu = 2, 343 | /// CD-ROM drive 344 | CdRom = 3, 345 | /// Sound Processing Unit 346 | Spu = 4, 347 | /// Extension port 348 | Pio = 5, 349 | /// Used to clear the ordering table 350 | Otc = 6, 351 | } 352 | 353 | impl Port { 354 | pub fn from_index(index: u32) -> Port { 355 | match index { 356 | 0 => Port::MDecIn, 357 | 1 => Port::MDecOut, 358 | 2 => Port::Gpu, 359 | 3 => Port::CdRom, 360 | 4 => Port::Spu, 361 | 5 => Port::Pio, 362 | 6 => Port::Otc, 363 | n => panic!("Invalid port {}", n), 364 | } 365 | } 366 | } 367 | 368 | impl From for SizedValue { 369 | fn from(v: Port) -> SizedValue { 370 | SizedValue(v as u32, 3) 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /src/parallel_io/exe_loader.rs: -------------------------------------------------------------------------------- 1 | //! Parallel I/O module used to load "naked" PlayStation 2 | //! executables. This doesn't emulate any real world hardware, it's 3 | //! inspired by mednafen's method of loading EXEs. 4 | 5 | use std::path::Path; 6 | use std::fs::File; 7 | use std::io; 8 | 9 | use cdrom::disc::Region; 10 | use bios::Bios; 11 | use assembler::Assembler; 12 | use assembler::syntax::*; 13 | use shared::SharedState; 14 | 15 | use super::ParallelIoModule; 16 | 17 | pub struct ExeLoader { 18 | /// Base address for the executable 19 | base: u32, 20 | /// Executable entry point 21 | entry: u32, 22 | /// GP value before jumping to the entry point 23 | initial_gp: u32, 24 | /// SP value before jumping to the entry point 25 | initial_sp: u32, 26 | /// Base address of the 0-filled area 27 | memfill_base: u32, 28 | /// Length of the 0-filled area 29 | memfill_len: u32, 30 | /// Region of the executable. `None` if the region for the 31 | /// executable couldn't be determined. 32 | region: Option, 33 | /// "text" section of the executable 34 | text: Vec, 35 | /// Index into `text` for the code FIFO 36 | text_index: usize, 37 | /// Code of the loader 38 | loader: Vec, 39 | } 40 | 41 | impl ExeLoader { 42 | pub fn load(r: &mut io::Read) -> Result { 43 | let mut buf = [0; 16]; 44 | 45 | try!(r.read_exact(&mut buf)); 46 | 47 | if &buf != b"PS-X EXE\0\0\0\0\0\0\0\0" { 48 | // Bad magic, this is not a PlayStation executable 49 | return Err(Error::UnknownFormat) 50 | } 51 | 52 | let entry = try!(read_u32(r)); 53 | 54 | let initial_gp = try!(read_u32(r)); 55 | 56 | let base = try!(read_u32(r)); 57 | 58 | let text_len = try!(read_u32(r)); 59 | 60 | // Let's be on the safe side and reject anormaly big 61 | // programs. Since the PlayStation RAM is 2MB big it doesn't 62 | // make sense to have bigger programs 63 | if text_len > 2 * 1024 * 1024 { 64 | return Err(Error::TooBig(text_len)); 65 | } 66 | 67 | // The next two words are Unknown/Unused in the No$ spec, 68 | // let's ignore them 69 | try!(read_u32(r)); 70 | try!(read_u32(r)); 71 | 72 | let memfill_base = try!(read_u32(r)); 73 | let memfill_len = try!(read_u32(r)); 74 | 75 | // For some reason the initial SP address comes with an 76 | // "offset" (per No$), not sure what that's for 77 | let initial_sp = try!(read_u32(r)) + try!(read_u32(r)); 78 | 79 | // The next 20bytes are padding 80 | try!(r.read_exact(&mut [0; 20])); 81 | 82 | // Skip the first part of the license string to get to the region 83 | try!(r.read_exact(&mut [0; 37])); 84 | 85 | let mut region_str = [0; 5]; 86 | 87 | try!(r.read_exact(&mut region_str)); 88 | 89 | let region = 90 | match ®ion_str { 91 | b"Japan" => Some(Region::Japan), 92 | b"Europ" => Some(Region::Europe), 93 | b"North" => Some(Region::NorthAmerica), 94 | // Unknown or missing region 95 | _ => None, 96 | }; 97 | 98 | // Read through all the huge padding 99 | try!(r.read_exact(&mut [0; 1930])); 100 | 101 | // Finally we can read the executable itself 102 | let mut text = vec![0; text_len as usize]; 103 | 104 | try!(r.read_exact(&mut text)); 105 | 106 | let mut loader = ExeLoader { 107 | base: base, 108 | entry: entry, 109 | initial_gp: initial_gp, 110 | initial_sp: initial_sp, 111 | memfill_base: memfill_base, 112 | memfill_len: memfill_len, 113 | region: region, 114 | text: text, 115 | text_index: 0, 116 | loader: Vec::new(), 117 | }; 118 | 119 | info!("Loaded PS-EXE: BASE=0x{:08x} ENTRY=0x{:08x} LEN={}", 120 | base, entry, text_len); 121 | 122 | loader.assemble_loader(); 123 | 124 | Ok(loader) 125 | } 126 | 127 | /// Assemble the code for the native loader whose purpose is to 128 | /// load the executable in RAM 129 | fn assemble_loader(&mut self) { 130 | let mut asm = Assembler::from_base(LOADER_ENTRY_ADDRESS); 131 | 132 | let irq_base = ::memory::map::IRQ_CONTROL.0; 133 | 134 | let cache_control = ::memory::map::CACHE_CONTROL.0; 135 | 136 | // First let's write a quick "clear_cache" function 137 | 138 | asm.assemble(&[ 139 | // Let's mask all interrupts before we start, the 140 | // executable will be free to re-enable them. 141 | Li(T0, irq_base), 142 | Sh(R0, T0, 4), 143 | 144 | // Let's start by taking care of the "memfill" region 145 | Li(T0, self.memfill_base), 146 | Li(T1, self.memfill_len), 147 | // Skip memfil if len is 0 148 | Beqz(T1, Label::Local("memfill_done", 'f')), 149 | // Set T1 to the end address 150 | Add(T1, T0, T1), 151 | 152 | Local("memfill_loop"), 153 | // Do the memfill one byte at a time, not very efficient 154 | // but that way we don't have to worry about alignment 155 | Addiu(T0, T0, 1), 156 | Bne(T0, T1, Label::Local("memfill_loop", 'b')), 157 | Sb(R0, T0, -1), 158 | 159 | Local("memfill_done"), 160 | 161 | // Now we can move on to copying the code from the 162 | // EXE_FIFO 163 | Li(T0, self.base), 164 | Li(T1, self.text.len() as u32), 165 | 166 | // We should probably not have no text to copy, but let's 167 | // be cautious 168 | Beqz(T1, Label::Local("text_copy_done", 'f')), 169 | 170 | // Set T1 to the end address 171 | Add(T1, T0, T1), 172 | // T2 to the location of the EXE FIFO 173 | Li(T2, EXE_FIFO_ADDRESS), 174 | 175 | Local("text_copy_loop"), 176 | // Load next text byte 177 | Lb(T3, T2, 0), 178 | Addiu(T0, T0, 1), 179 | Bne(T0, T1, Label::Local("text_copy_loop", 'b')), 180 | Sb(T3, T0, -1), 181 | 182 | Local("text_copy_done"), 183 | 184 | // Call the "clear_cache" function 185 | Jal(Label::Global("clear_cache")), 186 | Nop, 187 | 188 | // Finally we can load the register values and jump into 189 | // the EXE 190 | Li(GP, self.initial_gp), 191 | Li(SP, self.initial_sp), 192 | Li(T0, self.entry), 193 | 194 | Jalr(RA, T0), 195 | Nop, 196 | 197 | // We're done, there's probably no point in returning to 198 | // the caller, let's loop infinitely. If we wanted to 199 | // return to the caller we'd have to save the registers 200 | // (particularly SP) before jumping into the EXE so that 201 | // we can restore them here. 202 | Local("infinite_loop"), 203 | B(Label::Local("infinite_loop", 'b')), 204 | Nop, 205 | 206 | // Clear cache function 207 | Global("clear_cache"), 208 | Addiu(SP, SP, -24), 209 | Sw(RA, SP, 20), 210 | Sw(FP, SP, 16), 211 | Move(FP, SP), 212 | 213 | // First we need to move to KSEG1 (uncached region) 214 | La(T0, Label::Local("uncached", 'f')), 215 | Lui(T1, 0xa000), 216 | Or(T0, T0, T1), 217 | Jr(T0), 218 | Nop, 219 | 220 | Local("uncached"), 221 | // We're now running from uncached memory 222 | 223 | Li(T0, cache_control), 224 | Lw(T5, T0, 0), 225 | // Enable i-cache, set "tag test mode" 226 | Li(T1, 0x804), 227 | Sw(T1, T0, 0), 228 | 229 | // Isolate the cache 230 | Mfc0(T1, 12), 231 | Li(T2, 0x00010000), 232 | Or(T1, T1, T2), 233 | Mtc0(T1, 12), 234 | 235 | // Write 0 to each 4th word from 0 to 4095 to invalidate 236 | // each cacheline in the 4KB i-cache. 237 | Li(T1, 0x1000), 238 | Li(T2, 0), 239 | 240 | Local("icache_invalidate_loop"), 241 | Addiu(T2, T2, 16), 242 | Bne(T2, T1, Label::Local("icache_invalidate_loop", 'b')), 243 | Sw(R0, T2, -16), 244 | 245 | // De-isolate the cache 246 | Mfc0(T1, 12), 247 | Li(T2, !0x00010000), 248 | And(T1, T1, T2), 249 | Mtc0(T1, 12), 250 | 251 | // Clear tag test mode 252 | Li(T1, 0x800), 253 | Sw(T1, T0, 0), 254 | 255 | // Re-isolate the cache 256 | Mfc0(T1, 12), 257 | Li(T2, 0x00010000), 258 | Or(T1, T1, T2), 259 | Mtc0(T1, 12), 260 | 261 | // Write 0 to each word from 0 to 4095 to invalidate each 262 | // word in the 4KB i-cache. I don't think this is truly 263 | // necessary but the BIOS does it. 264 | Li(T1, 0x1000), 265 | Li(T2, 0), 266 | 267 | Local("icache_zero_loop"), 268 | Addiu(T2, T2, 4), 269 | Bne(T2, T1, Label::Local("icache_zero_loop", 'b')), 270 | Sw(R0, T2, -4), 271 | 272 | // De-isolate the cache 273 | Mfc0(T1, 12), 274 | Li(T2, !0x00010000), 275 | And(T1, T1, T2), 276 | Mtc0(T1, 12), 277 | 278 | // Restore cache control 279 | Sw(T5, T0, 0), 280 | 281 | // Return 282 | Move(SP, FP), 283 | Lw(RA, SP, 20), 284 | Lw(FP, SP, 16), 285 | Jr(RA), 286 | Addiu(SP, SP, 24) 287 | ]).unwrap(); 288 | 289 | let (mc, _) = asm.machine_code(); 290 | 291 | self.loader = mc; 292 | } 293 | 294 | pub fn load_file(path: &Path) -> Result { 295 | let mut f = try!(File::open(path)); 296 | 297 | ExeLoader::load(&mut f) 298 | } 299 | 300 | pub fn region(&self) -> Option { 301 | self.region 302 | } 303 | 304 | /// Patch the BIOS animation jump to run the loader code 305 | /// instead. Returns an error if the patching failed. 306 | pub fn patch_bios(&self, bios: &mut Bios) -> Result<(), ()> { 307 | let mut asm = Assembler::from_base(0); 308 | 309 | // Assemble the jump instruction 310 | let instruction = Jal(Label::Absolute(LOADER_ENTRY_ADDRESS)); 311 | 312 | asm.assemble(&[instruction]).unwrap(); 313 | 314 | let (mc, _) = asm.machine_code(); 315 | 316 | // It should only have generated a single instruction 317 | assert!(mc.len() == 4); 318 | 319 | // reassemble the instruction word 320 | let instruction = mc[0] as u32 321 | | ((mc[1] as u32) << 8) 322 | | ((mc[2] as u32) << 16) 323 | | ((mc[3] as u32) << 24); 324 | 325 | // Finally we can try to patch the BIOS 326 | bios.patch_animation_jump_hook(instruction) 327 | } 328 | } 329 | 330 | impl ParallelIoModule for ExeLoader { 331 | fn load(&mut self, _: &mut SharedState, offset: u32) -> u8 { 332 | if offset == EXE_FIFO_OFFSET { 333 | match self.text.get(self.text_index) { 334 | Some(&b) => { 335 | self.text_index += 1; 336 | b 337 | } 338 | None => { 339 | warn!("Load from EXE FIFO when empty"); 340 | !0 341 | } 342 | } 343 | } else if offset >= LOADER_ENTRY_OFFSET { 344 | let i = offset - LOADER_ENTRY_OFFSET; 345 | 346 | // There's nothing fater the loader, if we're out of 347 | // bounds we return full ones. 348 | *self.loader.get(i as usize).unwrap_or(&!0) 349 | } else { 350 | !0 351 | } 352 | } 353 | 354 | fn store(&mut self, _: &mut SharedState, _: u32, _: u8) { 355 | // NOP 356 | } 357 | } 358 | 359 | #[derive(Debug)] 360 | pub enum Error { 361 | /// Error while reading the save file 362 | IoError(io::Error), 363 | /// File is not in a known PlayStation executable format 364 | UnknownFormat, 365 | /// The program is anormaly large 366 | TooBig(u32), 367 | } 368 | 369 | impl From for Error { 370 | fn from(err: io::Error) -> Error { 371 | Error::IoError(err) 372 | } 373 | } 374 | 375 | fn read_u32(r: &mut io::Read) -> Result { 376 | let mut b = [0; 4]; 377 | 378 | try!(r.read_exact(&mut b)); 379 | 380 | Ok(b[0] as u32 381 | | ((b[1] as u32) << 8) 382 | | ((b[2] as u32) << 16) 383 | | ((b[3] as u32) << 24)) 384 | } 385 | 386 | /// Offset of the register containing the machine code FIFO for 387 | /// loading the EXE 388 | const EXE_FIFO_OFFSET: u32 = 0x100; 389 | 390 | /// Absolute address of the register containing the machine code FIFO 391 | /// for loading the EXE 392 | const EXE_FIFO_ADDRESS: u32 = ::memory::map::EXPANSION_1.0 + EXE_FIFO_OFFSET; 393 | 394 | /// Offset of the entry point for the loader code in the EXPANSION 1 395 | /// memory range 396 | const LOADER_ENTRY_OFFSET: u32 = 0x200; 397 | 398 | /// Absolute address of the entry point for the loader code 399 | const LOADER_ENTRY_ADDRESS: u32 = 400 | ::memory::map::EXPANSION_1.0 + LOADER_ENTRY_OFFSET; 401 | -------------------------------------------------------------------------------- /src/bios/db.rs: -------------------------------------------------------------------------------- 1 | //! BIOS database, lifted from mednafen 2 | 3 | use std::fmt; 4 | 5 | use shaman::digest::Digest; 6 | use shaman::sha2::Sha256; 7 | 8 | use cdrom::disc::Region; 9 | use assembler::Assembler; 10 | use assembler::syntax::*; 11 | 12 | use super::{Bios, BIOS_SIZE}; 13 | 14 | pub struct Metadata { 15 | pub sha256: [u8; 32], 16 | pub version_major: u8, 17 | pub version_minor: u8, 18 | pub region: Region, 19 | /// True if this dump is known to be bad 20 | pub known_bad: bool, 21 | /// ROM offset where the jump to the bootup logo animation code 22 | /// can be found. Replacing the word there with `0` (NOP). That's 23 | /// also where we can hook ourselves up to run extension code 24 | /// after the BIOS init code and before the CD gets loaded (this 25 | /// is what mednafen does). 26 | /// 27 | /// This value can be set to `None` if the correct address has not 28 | /// been determined for this particular BIOS. 29 | pub animation_jump_hook: Option, 30 | /// Method used to patch the BIOS to enable the debug UART or 31 | /// `None` if the method hasn't been found. 32 | pub patch_debug_uart: Option, 33 | } 34 | 35 | impl fmt::Debug for Metadata { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 | try!(write!(f, "{:?}/v{}.{}", 38 | self.region, self.version_major, self.version_minor)); 39 | 40 | if self.known_bad { 41 | try!(write!(f, "[BAD]")) 42 | } 43 | 44 | Ok(()) 45 | } 46 | } 47 | 48 | /// Attempt to find the metadata for the given BIOS binary blob. 49 | /// Returns None if this BIOS is not part of the database. 50 | pub fn lookup_blob(binary: &[u8; BIOS_SIZE]) -> Option<&'static Metadata> { 51 | 52 | let mut hasher = Sha256::new(); 53 | 54 | hasher.input(binary); 55 | 56 | let mut sha256 = [0; 32]; 57 | 58 | hasher.result(&mut sha256); 59 | 60 | lookup_sha256(&sha256) 61 | } 62 | 63 | /// Attempt to find the metadata for the given BIOS SHA-256 64 | /// hash. Returns None if this BIOS is not part of the database. 65 | pub fn lookup_sha256(sha256: &[u8; 32]) -> Option<&'static Metadata> { 66 | for md in &DATABASE { 67 | if md.sha256 == *sha256 { 68 | // Found match 69 | return Some(md); 70 | } 71 | } 72 | 73 | None 74 | } 75 | 76 | pub static DATABASE: [Metadata; 24] = [ 77 | Metadata { 78 | sha256: [0xcf, 0xc1, 0xfc, 0x38, 0xeb, 0x44, 0x2f, 0x6f, 79 | 0x80, 0x78, 0x14, 0x52, 0x11, 0x9e, 0x93, 0x1b, 80 | 0xca, 0xe2, 0x81, 0x00, 0xc1, 0xc9, 0x7e, 0x7e, 81 | 0x6c, 0x5f, 0x27, 0x25, 0xbb, 0xb0, 0xf8, 0xbb], 82 | version_major: 1, 83 | version_minor: 0, 84 | region: Region::Japan, 85 | known_bad: false, 86 | animation_jump_hook: None, 87 | patch_debug_uart: None, 88 | }, 89 | Metadata { 90 | sha256: [0x5e, 0xb3, 0xae, 0xe4, 0x95, 0x93, 0x75, 0x58, 91 | 0x31, 0x2b, 0x83, 0xb5, 0x43, 0x23, 0xd7, 0x6a, 92 | 0x4a, 0x01, 0x51, 0x90, 0xde, 0xcd, 0x40, 0x51, 93 | 0x21, 0x4f, 0x1b, 0x6d, 0xf0, 0x6a, 0xc3, 0x4b], 94 | version_major: 1, 95 | version_minor: 1, 96 | region: Region::Japan, 97 | known_bad: false, 98 | animation_jump_hook: None, 99 | patch_debug_uart: None, 100 | }, 101 | Metadata { 102 | sha256: [0x42, 0xe4, 0x12, 0x4b, 0xe7, 0x62, 0x3e, 0x2e, 103 | 0x28, 0xb1, 0xdb, 0x0d, 0x8d, 0x42, 0x65, 0x39, 104 | 0x64, 0x6f, 0xae, 0xe4, 0x9d, 0x74, 0xb7, 0x11, 105 | 0x66, 0xd8, 0xba, 0x5b, 0xd7, 0xc4, 0x72, 0xed], 106 | version_major: 2, 107 | version_minor: 0, 108 | region: Region::NorthAmerica, 109 | known_bad: false, 110 | animation_jump_hook: None, 111 | patch_debug_uart: None, 112 | }, 113 | Metadata { 114 | sha256: [0x0a, 0xf2, 0xbe, 0x34, 0x68, 0xd3, 0x0b, 0x60, 115 | 0x18, 0xb3, 0xc3, 0xb0, 0xd9, 0x8b, 0x8b, 0x64, 116 | 0x34, 0x7e, 0x25, 0x5e, 0x16, 0xd8, 0x74, 0xd5, 117 | 0x5f, 0x03, 0x63, 0x64, 0x89, 0x73, 0xdb, 0xf0], 118 | version_major: 2, 119 | version_minor: 0, 120 | region: Region::Europe, 121 | known_bad: false, 122 | animation_jump_hook: None, 123 | patch_debug_uart: None, 124 | }, 125 | Metadata { 126 | sha256: [0x6f, 0x71, 0xca, 0x1e, 0x71, 0x6d, 0xa7, 0x61, 127 | 0xdc, 0x53, 0x18, 0x7b, 0xd3, 0x9e, 0x00, 0xc2, 128 | 0x13, 0xf5, 0x66, 0xe5, 0x50, 0x90, 0x70, 0x8f, 129 | 0xd3, 0xe2, 0xb4, 0xb4, 0x25, 0xc8, 0xc9, 0x89], 130 | version_major: 2, 131 | version_minor: 1, 132 | region: Region::Japan, 133 | known_bad: false, 134 | animation_jump_hook: None, 135 | patch_debug_uart: None, 136 | }, 137 | Metadata { 138 | sha256: [0x6a, 0xd5, 0x52, 0x1d, 0x10, 0x5a, 0x6b, 0x86, 139 | 0x74, 0x1f, 0x1a, 0xf8, 0xda, 0x2e, 0x6e, 0xa1, 140 | 0xc7, 0x32, 0xd3, 0x44, 0x59, 0x94, 0x06, 0x18, 141 | 0xc7, 0x03, 0x05, 0xa1, 0x05, 0xe8, 0xec, 0x10], 142 | version_major: 2, 143 | version_minor: 1, 144 | region: Region::NorthAmerica, 145 | known_bad: false, 146 | animation_jump_hook: None, 147 | patch_debug_uart: None, 148 | }, 149 | Metadata { 150 | sha256: [0x1e, 0xfb, 0x0c, 0xfc, 0x5d, 0xb8, 0xa8, 0x75, 151 | 0x1a, 0x88, 0x4c, 0x53, 0x12, 0xe9, 0xc6, 0x26, 152 | 0x5c, 0xa1, 0xbc, 0x58, 0x0d, 0xc0, 0xc2, 0x66, 153 | 0x3e, 0xb2, 0xde, 0xa3, 0xbd, 0xe9, 0xfc, 0xf7], 154 | version_major: 2, 155 | version_minor: 1, 156 | region: Region::Europe, 157 | known_bad: false, 158 | animation_jump_hook: None, 159 | patch_debug_uart: None, 160 | }, 161 | Metadata { 162 | sha256: [0x0c, 0x83, 0x59, 0x87, 0x0c, 0xba, 0xc0, 0xea, 163 | 0x09, 0x1f, 0x1c, 0x87, 0xf1, 0x88, 0xcd, 0x33, 164 | 0x2d, 0xcc, 0x70, 0x97, 0x53, 0xb9, 0x1c, 0xaf, 165 | 0xd9, 0xfd, 0x44, 0xa4, 0xa6, 0x18, 0x81, 0x97], 166 | version_major: 2, 167 | version_minor: 2, 168 | region: Region::Japan, 169 | known_bad: false, 170 | animation_jump_hook: None, 171 | patch_debug_uart: None, 172 | }, 173 | Metadata { 174 | sha256: [0x8e, 0x03, 0x83, 0x17, 0x1e, 0x67, 0xb3, 0x3e, 175 | 0x60, 0xd5, 0xdf, 0x63, 0x94, 0xc5, 0x88, 0x43, 176 | 0xf3, 0xb1, 0x1c, 0x7a, 0x0b, 0x97, 0xf3, 0xbf, 177 | 0xcc, 0x43, 0x19, 0xac, 0x2d, 0x1f, 0x9d, 0x18], 178 | version_major: 2, 179 | version_minor: 2, 180 | region: Region::Japan, 181 | known_bad: true, 182 | animation_jump_hook: None, 183 | patch_debug_uart: None, 184 | }, 185 | Metadata { 186 | sha256: [0x71, 0xaf, 0x94, 0xd1, 0xe4, 0x7a, 0x68, 0xc1, 187 | 0x1e, 0x8f, 0xdb, 0x9f, 0x83, 0x68, 0x04, 0x06, 188 | 0x01, 0x51, 0x4a, 0x42, 0xa5, 0xa3, 0x99, 0xcd, 189 | 0xa4, 0x8c, 0x7d, 0x3b, 0xff, 0x1e, 0x99, 0xd3], 190 | version_major: 2, 191 | version_minor: 2, 192 | region: Region::NorthAmerica, 193 | known_bad: false, 194 | animation_jump_hook: None, 195 | patch_debug_uart: None, 196 | }, 197 | Metadata { 198 | sha256: [0x3d, 0x06, 0xd2, 0xc4, 0x69, 0x31, 0x3c, 0x2a, 199 | 0x21, 0x28, 0xd2, 0x4f, 0xe2, 0xe0, 0xc7, 0x1f, 200 | 0xf9, 0x9b, 0xc2, 0x03, 0x2b, 0xe8, 0x9a, 0x82, 201 | 0x9a, 0x62, 0x33, 0x71, 0x87, 0xf5, 0x00, 0xb7], 202 | version_major: 2, 203 | version_minor: 2, 204 | region: Region::Europe, 205 | known_bad: false, 206 | animation_jump_hook: None, 207 | patch_debug_uart: None, 208 | }, 209 | Metadata { 210 | sha256: [0x40, 0x18, 0x74, 0x9b, 0x36, 0x98, 0xb8, 0x69, 211 | 0x43, 0x87, 0xbe, 0xeb, 0xcb, 0xab, 0xfb, 0x48, 212 | 0x47, 0x05, 0x13, 0x06, 0x68, 0x40, 0xf9, 0x44, 213 | 0x14, 0x59, 0xee, 0x4c, 0x9f, 0x0f, 0x39, 0xbc], 214 | version_major: 2, 215 | version_minor: 2, 216 | region: Region::Japan, 217 | known_bad: false, 218 | animation_jump_hook: None, 219 | patch_debug_uart: None, 220 | }, 221 | Metadata { 222 | sha256: [0x9c, 0x04, 0x21, 0x85, 0x8e, 0x21, 0x78, 0x05, 223 | 0xf4, 0xab, 0xe1, 0x86, 0x98, 0xaf, 0xea, 0x8d, 224 | 0x5a, 0xa3, 0x6f, 0xf0, 0x72, 0x7e, 0xb8, 0x48, 225 | 0x49, 0x44, 0xe0, 0x0e, 0xb5, 0xe7, 0xea, 0xdb], 226 | version_major: 3, 227 | version_minor: 0, 228 | region: Region::Japan, 229 | known_bad: false, 230 | animation_jump_hook: None, 231 | patch_debug_uart: None, 232 | }, 233 | Metadata { 234 | sha256: [0x11, 0x05, 0x2b, 0x64, 0x99, 0xe4, 0x66, 0xbb, 235 | 0xf0, 0xa7, 0x09, 0xb1, 0xf9, 0xcb, 0x68, 0x34, 236 | 0xa9, 0x41, 0x8e, 0x66, 0x68, 0x03, 0x87, 0x91, 237 | 0x24, 0x51, 0xe9, 0x71, 0xcf, 0x8a, 0x1f, 0xef], 238 | version_major: 3, 239 | version_minor: 0, 240 | region: Region::NorthAmerica, 241 | known_bad: false, 242 | animation_jump_hook: Some(0x6990), 243 | patch_debug_uart: Some(patch_debug_uart_na_30), 244 | }, 245 | Metadata { 246 | sha256: [0x1f, 0xaa, 0xa1, 0x8f, 0xa8, 0x20, 0xa0, 0x22, 247 | 0x5e, 0x48, 0x8d, 0x9f, 0x08, 0x62, 0x96, 0xb8, 248 | 0xe6, 0xc4, 0x6d, 0xf7, 0x39, 0x66, 0x60, 0x93, 249 | 0x98, 0x7f, 0xf7, 0xd8, 0xfd, 0x35, 0x2c, 0x09], 250 | version_major: 3, 251 | version_minor: 0, 252 | region: Region::Europe, 253 | known_bad: false, 254 | animation_jump_hook: None, 255 | patch_debug_uart: None, 256 | }, 257 | Metadata { 258 | sha256: [0x9e, 0x1f, 0x8f, 0xb4, 0xfa, 0x35, 0x6a, 0x5a, 259 | 0xc2, 0x9d, 0x7c, 0x72, 0x09, 0x62, 0x6d, 0xcc, 260 | 0x1b, 0x30, 0x38, 0xc0, 0xe5, 0xa8, 0x5b, 0x0e, 261 | 0x99, 0xd1, 0xdb, 0x96, 0x92, 0x66, 0x47, 0xca], 262 | version_major: 3, 263 | version_minor: 0, 264 | region: Region::Europe, 265 | known_bad: true, 266 | animation_jump_hook: None, 267 | patch_debug_uart: None, 268 | }, 269 | Metadata { 270 | sha256: [0xe9, 0x00, 0x50, 0x4d, 0x17, 0x55, 0xf0, 0x21, 271 | 0xf8, 0x61, 0xb8, 0x2c, 0x82, 0x58, 0xc5, 0xe6, 272 | 0x65, 0x8c, 0x7b, 0x59, 0x2f, 0x80, 0x0c, 0xcc, 273 | 0xd9, 0x1f, 0x5d, 0x32, 0xea, 0x38, 0x0d, 0x28], 274 | version_major: 4, 275 | version_minor: 0, 276 | region: Region::Japan, 277 | known_bad: false, 278 | // Same patch as NA/3.0 279 | animation_jump_hook: Some(0x6990), 280 | patch_debug_uart: Some(patch_debug_uart_na_30), 281 | }, 282 | Metadata { 283 | sha256: [0xb3, 0xaa, 0x63, 0xcf, 0x30, 0xc8, 0x1e, 0x0a, 284 | 0x40, 0x64, 0x17, 0x40, 0xf4, 0xa4, 0x3e, 0x25, 285 | 0xfd, 0xa0, 0xb2, 0x1b, 0x79, 0x2f, 0xa9, 0xaa, 286 | 0xef, 0x60, 0xce, 0x16, 0x75, 0x76, 0x14, 0x79], 287 | version_major: 4, 288 | version_minor: 1, 289 | region: Region::Japan, 290 | known_bad: false, 291 | animation_jump_hook: None, 292 | patch_debug_uart: None, 293 | }, 294 | Metadata { 295 | sha256: [0x39, 0xdc, 0xc1, 0xa0, 0x71, 0x70, 0x36, 0xc9, 296 | 0xb6, 0xac, 0x52, 0xfe, 0xfd, 0x1e, 0xe7, 0xa5, 297 | 0x7d, 0x38, 0x08, 0xe8, 0xcf, 0xbc, 0x75, 0x58, 298 | 0x79, 0xfa, 0x68, 0x5a, 0x0a, 0x73, 0x82, 0x78], 299 | version_major: 4, 300 | version_minor: 1, 301 | region: Region::NorthAmerica, 302 | known_bad: false, 303 | animation_jump_hook: None, 304 | patch_debug_uart: None, 305 | }, 306 | Metadata { 307 | sha256: [0x5e, 0x84, 0xa9, 0x48, 0x18, 0xcf, 0x52, 0x82, 308 | 0xf4, 0x21, 0x75, 0x91, 0xfe, 0xfd, 0x88, 0xbe, 309 | 0x36, 0xb9, 0xb1, 0x74, 0xb3, 0xcc, 0x7c, 0xb0, 310 | 0xbc, 0xd7, 0x51, 0x99, 0xbe, 0xb4, 0x50, 0xf1], 311 | version_major: 4, 312 | version_minor: 1, 313 | region: Region::Europe, 314 | known_bad: false, 315 | animation_jump_hook: None, 316 | patch_debug_uart: None, 317 | }, 318 | Metadata { 319 | sha256: [0xb2, 0x9b, 0x4b, 0x5f, 0xcd, 0xde, 0xf3, 0x69, 320 | 0xbd, 0x66, 0x40, 0xac, 0xac, 0xda, 0x08, 0x65, 321 | 0xe0, 0x36, 0x6f, 0xcf, 0x7e, 0xa5, 0x4e, 0x40, 322 | 0xb2, 0xf1, 0xa8, 0x17, 0x80, 0x04, 0xf8, 0x9a], 323 | version_major: 4, 324 | version_minor: 3, 325 | region: Region::Japan, 326 | known_bad: false, 327 | animation_jump_hook: None, 328 | patch_debug_uart: None, 329 | }, 330 | Metadata { 331 | sha256: [0x5c, 0x01, 0x66, 0xda, 0x24, 0xe2, 0x7d, 0xea, 332 | 0xa8, 0x22, 0x46, 0xde, 0x8f, 0xf0, 0x10, 0x82, 333 | 0x67, 0xfe, 0x4b, 0xb5, 0x9f, 0x6d, 0xf0, 0xfd, 334 | 0xec, 0x50, 0xe0, 0x5e, 0x62, 0x44, 0x8c, 0xa4], 335 | version_major: 4, 336 | version_minor: 4, 337 | region: Region::Europe, 338 | known_bad: false, 339 | animation_jump_hook: None, 340 | patch_debug_uart: None, 341 | }, 342 | Metadata { 343 | sha256: [0xac, 0xa9, 0xcb, 0xfa, 0x97, 0x4b, 0x93, 0x36, 344 | 0x46, 0xba, 0xad, 0x65, 0x56, 0xa8, 0x67, 0xec, 345 | 0xa9, 0xb8, 0x1c, 0xe6, 0x5d, 0x8a, 0xf3, 0x43, 346 | 0xa7, 0x84, 0x3f, 0x77, 0x75, 0xb9, 0xff, 0xc8], 347 | version_major: 4, 348 | version_minor: 5, 349 | region: Region::NorthAmerica, 350 | known_bad: false, 351 | animation_jump_hook: None, 352 | patch_debug_uart: None, 353 | }, 354 | Metadata { 355 | sha256: [0x42, 0x24, 0x4b, 0x0c, 0x65, 0x08, 0x21, 0x51, 356 | 0x97, 0x51, 0xb7, 0xe7, 0x7a, 0xd1, 0xd3, 0x22, 357 | 0x2a, 0x01, 0x25, 0xe7, 0x55, 0x86, 0xdf, 0x2b, 358 | 0x4e, 0x84, 0xba, 0x69, 0x3b, 0x98, 0x09, 0xdc], 359 | version_major: 4, 360 | version_minor: 5, 361 | region: Region::Europe, 362 | known_bad: false, 363 | animation_jump_hook: None, 364 | patch_debug_uart: None, 365 | }, 366 | ]; 367 | 368 | fn patch_debug_uart_na_30(bios: &mut Bios) { 369 | // At offset 0x6f0c the BIOS does: 370 | // 371 | // lui $at,0xa001 372 | // jal 0xbfc06784 373 | // sw zero,-18000($at) 374 | // 375 | // In other wordss it stores 0 at 0xa000b9b0 which disables the 376 | // UART. We need to change this value to 1 to enable 377 | // it. Fortunately we can use $gp as SW base address since its 378 | // value at this point is equal to 0xa0010ff0. 379 | // 380 | // Credit to the No$ spec for documenting this hack. 381 | 382 | let mut asm = Assembler::from_base(0); 383 | 384 | asm.assemble(&[ 385 | Li(AT, 1), 386 | Jal(Label::Absolute(0xbfc06784)), 387 | Sw(AT, GP, -18000 - 0xff0), 388 | ]).unwrap(); 389 | 390 | let (mc, _) = asm.machine_code(); 391 | 392 | assert!(mc.len() == 12); 393 | 394 | for (i, &b) in mc.iter().enumerate() { 395 | bios.data[0x6f0c + i] = b; 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /src/padmemcard/mod.rs: -------------------------------------------------------------------------------- 1 | //! Gamepad and memory card controller emulation 2 | 3 | use memory::Addressable; 4 | use interrupt::Interrupt; 5 | use timekeeper::{Peripheral, Cycles}; 6 | use shared::SharedState; 7 | use tracer::module_tracer; 8 | 9 | use self::gamepad::GamePad; 10 | 11 | pub mod gamepad; 12 | 13 | #[derive(RustcDecodable, RustcEncodable)] 14 | pub struct PadMemCard { 15 | /// Serial clock divider. The LSB is read/write but is not used, 16 | /// This way the hardware divide the CPU clock by half of 17 | /// `baud_div` and can invert the serial clock polarity twice 18 | /// every `baud_div` which effectively means that the resulting 19 | /// frequency is CPU clock / (`baud_div` & 0xfe). 20 | baud_div: u16, 21 | /// Serial config, not implemented for now... 22 | mode: u8, 23 | /// Transmission enabled if true 24 | tx_en: bool, 25 | /// If true the targeted peripheral select signal is asserted (the 26 | /// actual signal is active low, so it's driving low on the 27 | /// controller port when `select` is true). The `target` field 28 | /// says which peripheral is addressed. 29 | select: bool, 30 | /// This bit says which of the two pad/memorycard port pair 31 | /// we're selecting with `select_n` above. Multitaps are handled 32 | /// at the serial protocol level, not by dedicated hardware pins. 33 | target: Target, 34 | /// Control register bits 3 and 5 are read/write but I don't know 35 | /// what they do. I just same them here for accurate readback. 36 | unknown: u8, 37 | /// XXX not sure what this does exactly, forces a read without any 38 | /// TX? 39 | rx_en: bool, 40 | /// Data Set Ready signal, active low (driven by the gamepad) 41 | dsr: bool, 42 | /// If true an interrupt is generated when a DSR pulse is received 43 | /// from the pad/memory card 44 | dsr_it: bool, 45 | /// Current interrupt level 46 | interrupt: bool, 47 | /// Current response byte. 48 | /// XXX Normally it should be a FIFO but I'm not sure how it works 49 | /// really. Besides the game should check for the response after 50 | /// each byte anyway, so it's probably unused the vast majority of 51 | /// times. 52 | response: u8, 53 | /// True when we the RX FIFO is not empty. 54 | rx_not_empty: bool, 55 | /// Gamepad in slot 1 56 | pad1: GamePad, 57 | /// Gamepad in slot 2 58 | pad2: GamePad, 59 | /// Bus state machine 60 | bus: BusState, 61 | } 62 | 63 | impl PadMemCard { 64 | pub fn new() -> PadMemCard { 65 | PadMemCard { 66 | baud_div: 0, 67 | mode: 0, 68 | tx_en: false, 69 | select: false, 70 | target: Target::PadMemCard1, 71 | interrupt: false, 72 | unknown: 0, 73 | rx_en: false, 74 | dsr: false, 75 | dsr_it: false, 76 | response: 0xff, 77 | rx_not_empty: false, 78 | pad1: GamePad::disconnected(), 79 | pad2: GamePad::disconnected(), 80 | bus: BusState::Idle, 81 | } 82 | } 83 | 84 | pub fn store(&mut self, 85 | shared: &mut SharedState, 86 | offset: u32, 87 | val: u32) { 88 | 89 | module_tracer("PAD_MEMCARD", |m| { 90 | let now = shared.tk().now(); 91 | 92 | m.trace(now, "w_offset", offset as u8); 93 | m.trace(now, "w_size", T::size()); 94 | m.trace(now, "w_value", val); 95 | }); 96 | 97 | self.sync(shared); 98 | 99 | match offset { 100 | 0 => { 101 | if T::size() != 1 { 102 | panic!("Unhandled gamepad TX access ({})", 103 | T::size()); 104 | } 105 | 106 | self.send_command(shared, val as u8); 107 | } 108 | 8 => self.set_mode(val as u8), 109 | 10 => { 110 | if T::size() == 1 { 111 | // Byte access behaves like a halfword 112 | panic!("Unhandled byte gamepad control access"); 113 | } 114 | self.set_control(shared, val as u16); 115 | } 116 | 14 => self.baud_div = val as u16, 117 | _ => panic!("Unhandled write to gamepad register {} {:04x}", 118 | offset, val as u16), 119 | } 120 | } 121 | 122 | pub fn load(&mut self, 123 | shared: &mut SharedState, 124 | offset: u32) -> u32 { 125 | 126 | module_tracer("PAD_MEMCARD", |m| { 127 | let now = shared.tk().now(); 128 | 129 | m.trace(now, "r_offset", offset as u8); 130 | m.trace(now, "r_size", T::size()); 131 | }); 132 | 133 | self.sync(shared); 134 | 135 | match offset { 136 | 0 => { 137 | if T::size() != 1 { 138 | panic!("Unhandled gamepad RX access ({})", 139 | T::size()); 140 | } 141 | 142 | let res = self.response as u32; 143 | 144 | self.rx_not_empty = false; 145 | self.response = 0xff; 146 | 147 | res 148 | } 149 | 4 => { 150 | self.stat() 151 | } 152 | 8 => self.mode as u32, 153 | 10 => self.control() as u32, 154 | 14 => self.baud_div as u32, 155 | _ => panic!("Unhandled gamepad read {:?} 0x{:x}", 156 | T::size(), offset), 157 | } 158 | } 159 | 160 | pub fn sync(&mut self, 161 | shared: &mut SharedState) { 162 | 163 | let delta = shared.tk().sync(Peripheral::PadMemCard); 164 | 165 | match self.bus { 166 | BusState::Idle => 167 | shared.tk().no_sync_needed(Peripheral::PadMemCard), 168 | BusState::Transfer(r, dsr, delay) => { 169 | if delta < delay { 170 | let delay = delay - delta; 171 | self.bus = BusState::Transfer(r, dsr, delay); 172 | 173 | if self.dsr_it { 174 | shared.tk().set_next_sync_delta(Peripheral::PadMemCard, 175 | delay); 176 | } else { 177 | shared.tk().no_sync_needed(Peripheral::PadMemCard); 178 | } 179 | } else { 180 | // We reached the end of the transfer 181 | 182 | if self.rx_not_empty { 183 | // XXX should push in the non-emulated RX FIFO 184 | // instead of overwritting `self.response` 185 | panic!("Gamepad RX while FIFO isn't empty"); 186 | } 187 | 188 | self.response = r; 189 | self.rx_not_empty = true; 190 | self.dsr = dsr; 191 | 192 | if self.dsr { 193 | if self.dsr_it { 194 | if !self.interrupt { 195 | // Rising edge of the interrupt 196 | let irq_state = shared.irq_state_mut(); 197 | 198 | irq_state.assert(Interrupt::PadMemCard); 199 | } 200 | 201 | self.interrupt = true; 202 | } 203 | 204 | // The DSR pulse is generated purely by the 205 | // controller without any input from the 206 | // console. Therefore the actual length of the 207 | // pulse changes from controller to 208 | // controller. I have two seemingly identical 209 | // SCPH-1080 controllers, one pulses the DSR 210 | // line for ~100CPU cycles while the other one 211 | // is slightly faster at around ~90 CPU 212 | // cycles. 213 | 214 | // XXX Because of timing inaccuracies 215 | // throughout the emulator I can't use the 216 | // proper timing otherwise the BIOS attempts 217 | // to ack the interrupt while DSR is still 218 | // active. 219 | let dsr_duration = 10; 220 | self.bus = BusState::Dsr(dsr_duration); 221 | } else { 222 | // We're done with this transaction 223 | self.bus = BusState::Idle; 224 | } 225 | 226 | shared.tk().no_sync_needed(Peripheral::PadMemCard); 227 | } 228 | } 229 | BusState::Dsr(delay) => { 230 | if delta < delay { 231 | let delay = delay - delta; 232 | self.bus = BusState::Dsr(delay); 233 | } else { 234 | // DSR pulse is over, bus is idle 235 | self.dsr = false; 236 | self.bus = BusState::Idle; 237 | } 238 | shared.tk().no_sync_needed(Peripheral::PadMemCard); 239 | } 240 | } 241 | } 242 | 243 | /// Return a mutable reference to the gamepad profiles being used. 244 | pub fn gamepads_mut(&mut self) -> [&mut GamePad; 2] { 245 | [ &mut self.pad1, &mut self.pad2 ] 246 | } 247 | 248 | fn send_command(&mut self, shared: &mut SharedState, cmd: u8) { 249 | if !self.tx_en { 250 | // It should be stored in the FIFO and sent when tx_en is 251 | // set (I think) 252 | panic!("Unhandled gamepad command while tx_en is disabled"); 253 | } 254 | 255 | if self.bus.is_busy() { 256 | // I suppose the transfer should be queued in the TX FIFO? 257 | warn!("Gamepad command {:x} while bus is busy!", cmd); 258 | } 259 | 260 | let (response, dsr) = 261 | if self.select { 262 | match self.target { 263 | Target::PadMemCard1 => self.pad1.send_command(cmd), 264 | Target::PadMemCard2 => self.pad2.send_command(cmd), 265 | } 266 | } else { 267 | // No response 268 | (0xff, false) 269 | }; 270 | 271 | // XXX Handle `mode` as well, especially the "baudrate reload 272 | // factor". For now I assume we're sending 8 bits, one every 273 | // `baud_div` CPU cycles. 274 | let tx_duration = 8 * self.baud_div as Cycles; 275 | 276 | self.bus = BusState::Transfer(response, dsr, tx_duration); 277 | 278 | // XXX For now pretend that the DSR pulse follows 279 | // immediately after the last byte, probably not accurate. 280 | shared.tk().set_next_sync_delta(Peripheral::PadMemCard, tx_duration); 281 | } 282 | 283 | fn stat(&self) -> u32 { 284 | let mut stat = 0u32; 285 | 286 | // TX Ready bits 1 and 2 (Not sure when they go low) 287 | stat |= 5; 288 | stat |= (self.rx_not_empty as u32) << 1; 289 | // RX parity error should always be 0 in our case. 290 | stat |= 0 << 3; 291 | stat |= (self.dsr as u32) << 7; 292 | stat |= (self.interrupt as u32) << 9; 293 | // XXX needs to add the baudrate counter in bits [31:11]; 294 | stat |= 0 << 11; 295 | 296 | stat 297 | } 298 | 299 | fn set_mode(&mut self, mode: u8) { 300 | self.mode = mode; 301 | } 302 | 303 | fn control(&self) -> u16 { 304 | let mut ctrl = 0u16; 305 | 306 | ctrl |= self.unknown as u16; 307 | 308 | ctrl |= (self.tx_en as u16) << 0; 309 | ctrl |= (self.select as u16) << 1; 310 | // XXX I assume this flag self-resets? When? 311 | ctrl |= (self.rx_en as u16) << 2; 312 | // XXX Add other interrupts when they're implemented 313 | ctrl |= (self.dsr_it as u16) << 12; 314 | ctrl |= (self.target as u16) << 13; 315 | 316 | ctrl 317 | } 318 | 319 | fn set_control(&mut self, shared: &mut SharedState, ctrl: u16) { 320 | if ctrl & 0x40 != 0 { 321 | // Soft reset 322 | self.baud_div = 0; 323 | self.mode = 0; 324 | self.select = false; 325 | self.target = Target::PadMemCard1; 326 | self.unknown = 0; 327 | self.interrupt = false; 328 | self.rx_not_empty = false; 329 | self.bus = BusState::Idle; 330 | // XXX since the gamepad/memory card asserts this signal 331 | // it actually probably shouldn't release here but it'll 332 | // make our state machine simpler for the time being. 333 | self.dsr = false; 334 | 335 | // It doesn't seem to reset the contents of the RX FIFO. 336 | } else { 337 | if ctrl & 0x10 != 0 { 338 | // Interrupt acknowledge 339 | 340 | self.interrupt = false; 341 | 342 | if self.dsr && self.dsr_it { 343 | // The controller's "dsr_it" interrupt is not edge 344 | // triggered: as long as self.dsr && self.dsr_it 345 | // is true it will keep being triggered. If the 346 | // software attempts to acknowledge the interrupt 347 | // in this state it will re-trigger immediately 348 | // which will be seen by the edge-triggered top 349 | // level interrupt controller. So I guess this 350 | // shouldn't happen? 351 | warn!("Gamepad interrupt acknowledge while DSR is active"); 352 | 353 | self.interrupt = true; 354 | shared.irq_state_mut().assert(Interrupt::PadMemCard); 355 | } 356 | } 357 | 358 | let prev_select = self.select; 359 | 360 | // No idea what bits 3 and 5 do but they're read/write. 361 | self.unknown = (ctrl as u8) & 0x28; 362 | 363 | self.tx_en = ctrl & 1 != 0; 364 | self.select = (ctrl >> 1) & 1 != 0; 365 | self.rx_en = (ctrl >> 2) & 1 != 0; 366 | self.dsr_it = (ctrl >> 12) & 1 != 0; 367 | self.target = Target::from_control(ctrl); 368 | 369 | if self.rx_en { 370 | panic!("Gamepad rx_en not implemented"); 371 | } 372 | 373 | if self.dsr_it && !self.interrupt && self.dsr { 374 | // Interrupt should trigger here but that really 375 | // shouldn't happen I think. 376 | panic!("dsr_it enabled while DSR signal is active"); 377 | } 378 | 379 | if ctrl & 0xf00 != 0 { 380 | // XXX add support for those interrupts 381 | panic!("Unsupported gamepad interrupts: {:04x}", ctrl); 382 | } 383 | 384 | if !prev_select && self.select { 385 | // XXX Should probably also check self.target, not 386 | // sure how it influences the select line. I assume 387 | // only the targeted slot is selected? 388 | self.pad1.select(); 389 | } 390 | } 391 | } 392 | } 393 | 394 | /// Identifies the target of the serial communication, either the 395 | /// gamepad/memory card port 0 or 1. 396 | #[derive(Clone, Copy, PartialEq, Eq, RustcDecodable, RustcEncodable)] 397 | enum Target { 398 | PadMemCard1 = 0, 399 | PadMemCard2 = 1, 400 | } 401 | 402 | impl Target { 403 | fn from_control(ctrl: u16) -> Target { 404 | match ctrl & 0x2000 != 0 { 405 | false => Target::PadMemCard1, 406 | true => Target::PadMemCard2, 407 | } 408 | } 409 | } 410 | 411 | /// Controller transaction state machine 412 | #[derive(Debug, RustcDecodable, RustcEncodable)] 413 | enum BusState { 414 | /// Bus is idle 415 | Idle, 416 | /// Transaction in progress, we store the response byte, the DSR 417 | /// response and the number of Cycles remaining until we reach the 418 | /// DSR pulse (if any) 419 | Transfer(u8, bool, Cycles), 420 | /// DSR is asserted, count the number of cycles remaining. 421 | Dsr(Cycles), 422 | } 423 | 424 | impl BusState { 425 | fn is_busy(&self) -> bool { 426 | match *self { 427 | BusState::Idle => false, 428 | _ => true, 429 | } 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /src/spu/mod.rs: -------------------------------------------------------------------------------- 1 | use rustc_serialize::{Decodable, Encodable, Decoder, Encoder}; 2 | 3 | use memory::Addressable; 4 | 5 | /// Sound Processing Unit 6 | pub struct Spu { 7 | /// Most of the SPU registers are not updated by the hardware, 8 | /// their value is just moved to the internal registers when 9 | /// needed. Therefore we can emulate those registers like a RAM of 10 | /// sorts. 11 | shadow_registers: [u16; 0x100], 12 | 13 | /// SPU RAM: 256k 16bit samples 14 | ram: Box<[u16; 256 * 1024]>, 15 | /// Write pointer in the SPU RAM 16 | ram_index: u32, 17 | } 18 | 19 | impl Spu { 20 | pub fn new() -> Spu { 21 | Spu { 22 | shadow_registers: [0; 0x100], 23 | ram: box_array![0xbad; 256 * 1024], 24 | ram_index: 0, 25 | } 26 | } 27 | 28 | pub fn store(&mut self, offset: u32, val: u32) { 29 | if T::size() != 2 { 30 | panic!("Unhandled SPU store ({})", T::size()); 31 | } 32 | 33 | let val = val as u16; 34 | 35 | // Convert into a halfword index 36 | let index = (offset >> 1) as usize; 37 | 38 | if index < 0xc0 { 39 | match index & 7 { 40 | regmap::voice::VOLUME_LEFT => (), 41 | regmap::voice::VOLUME_RIGHT => (), 42 | regmap::voice::ADPCM_SAMPLE_RATE => (), 43 | regmap::voice::ADPCM_START_INDEX => (), 44 | regmap::voice::ADPCM_ADSR_LOW => (), 45 | regmap::voice::ADPCM_ADSR_HIGH => (), 46 | // XXX change current volume? 47 | regmap::voice::CURRENT_ADSR_VOLUME => (), 48 | regmap::voice::ADPCM_REPEAT_INDEX => (), 49 | _ => unreachable!(), 50 | } 51 | } else { 52 | match index { 53 | regmap::MAIN_VOLUME_LEFT => (), 54 | regmap::MAIN_VOLUME_RIGHT => (), 55 | regmap::REVERB_VOLUME_LEFT => (), 56 | regmap::REVERB_VOLUME_RIGHT => (), 57 | regmap::VOICE_ON_LOW => 58 | self.shadow_registers[regmap::VOICE_STATUS_LOW] |= val, 59 | regmap::VOICE_ON_HIGH => 60 | self.shadow_registers[regmap::VOICE_STATUS_HIGH] |= val, 61 | regmap::VOICE_OFF_LOW => 62 | self.shadow_registers[regmap::VOICE_STATUS_LOW] &= !val, 63 | regmap::VOICE_OFF_HIGH => 64 | self.shadow_registers[regmap::VOICE_STATUS_HIGH] &= !val, 65 | regmap::VOICE_PITCH_MOD_EN_LOW => (), 66 | regmap::VOICE_PITCH_MOD_EN_HIGH => (), 67 | regmap::VOICE_NOISE_EN_LOW => (), 68 | regmap::VOICE_NOISE_EN_HIGH => (), 69 | regmap::VOICE_REVERB_EN_LOW => (), 70 | regmap::VOICE_REVERB_EN_HIGH => (), 71 | regmap::VOICE_STATUS_LOW => (), 72 | regmap::VOICE_STATUS_HIGH => (), 73 | regmap::REVERB_BASE => (), 74 | regmap::TRANSFER_START_INDEX => 75 | self.ram_index = (val as u32) << 2, 76 | regmap::TRANSFER_FIFO => 77 | self.fifo_write(val), 78 | regmap::CONTROL => 79 | self.set_control(val), 80 | regmap::TRANSFER_CONTROL => 81 | self.set_transfer_control(val), 82 | regmap::CD_VOLUME_LEFT => (), 83 | regmap::CD_VOLUME_RIGHT => (), 84 | regmap::EXT_VOLUME_LEFT => (), 85 | regmap::EXT_VOLUME_RIGHT => (), 86 | regmap::REVERB_APF_OFFSET1 => (), 87 | regmap::REVERB_APF_OFFSET2 => (), 88 | regmap::REVERB_REFLECT_VOLUME1 => (), 89 | regmap::REVERB_COMB_VOLUME1 => (), 90 | regmap::REVERB_COMB_VOLUME2 => (), 91 | regmap::REVERB_COMB_VOLUME3 => (), 92 | regmap::REVERB_COMB_VOLUME4 => (), 93 | regmap::REVERB_REFLECT_VOLUME2 => (), 94 | regmap::REVERB_APF_VOLUME1 => (), 95 | regmap::REVERB_APF_VOLUME2 => (), 96 | regmap::REVERB_REFLECT_SAME_LEFT1 => (), 97 | regmap::REVERB_REFLECT_SAME_RIGHT1 => (), 98 | regmap::REVERB_COMB_LEFT1 => (), 99 | regmap::REVERB_COMB_RIGHT1 => (), 100 | regmap::REVERB_COMB_LEFT2 => (), 101 | regmap::REVERB_COMB_RIGHT2 => (), 102 | regmap::REVERB_REFLECT_SAME_LEFT2 => (), 103 | regmap::REVERB_REFLECT_SAME_RIGHT2 => (), 104 | regmap::REVERB_REFLECT_DIFF_LEFT1 => (), 105 | regmap::REVERB_REFLECT_DIFF_RIGHT1 => (), 106 | regmap::REVERB_COMB_LEFT3 => (), 107 | regmap::REVERB_COMB_RIGHT3 => (), 108 | regmap::REVERB_COMB_LEFT4 => (), 109 | regmap::REVERB_COMB_RIGHT4 => (), 110 | regmap::REVERB_REFLECT_DIFF_LEFT2 => (), 111 | regmap::REVERB_REFLECT_DIFF_RIGHT2 => (), 112 | regmap::REVERB_APF_LEFT1 => (), 113 | regmap::REVERB_APF_RIGHT1 => (), 114 | regmap::REVERB_APF_LEFT2 => (), 115 | regmap::REVERB_APF_RIGHT2 => (), 116 | regmap::REVERB_INPUT_VOLUME_LEFT => (), 117 | regmap::REVERB_INPUT_VOLUME_RIGHT => (), 118 | _ => panic!("Unhandled SPU store {:x} {:04x}", offset, val), 119 | } 120 | } 121 | 122 | if index < 0x100 { 123 | self.shadow_registers[index] = val; 124 | } 125 | } 126 | 127 | pub fn load(&mut self, offset: u32) -> u32 { 128 | if T::size() != 2 { 129 | panic!("Unhandled SPU load ({})", T::size()); 130 | } 131 | 132 | let index = (offset >> 1) as usize; 133 | 134 | if index >= 0x100 { 135 | // XXX Support SPU internal registers 136 | return 0; 137 | } 138 | 139 | let shadow = self.shadow_registers[index]; 140 | 141 | // XXX This is a bit ugly but I use the match to "whitelist" 142 | // shadow registers as I encounter them. Once all registers 143 | // are correctly implemented we can default to the shadow. 144 | let r = 145 | if index < 0xc0 { 146 | match index & 7 { 147 | regmap::voice::CURRENT_ADSR_VOLUME => 148 | // XXX return current volume 149 | shadow, 150 | regmap::voice::ADPCM_REPEAT_INDEX => 151 | // XXX return current repeat index 152 | shadow, 153 | _ => shadow, 154 | } 155 | } else { 156 | match (offset >> 1) as usize { 157 | regmap::MAIN_VOLUME_LEFT => shadow, 158 | regmap::MAIN_VOLUME_RIGHT => shadow, 159 | regmap::VOICE_ON_LOW => shadow, 160 | regmap::VOICE_ON_HIGH => shadow, 161 | regmap::VOICE_OFF_LOW => shadow, 162 | regmap::VOICE_OFF_HIGH => shadow, 163 | regmap::VOICE_NOISE_EN_HIGH => shadow, 164 | regmap::VOICE_NOISE_EN_LOW => shadow, 165 | regmap::VOICE_REVERB_EN_LOW => shadow, 166 | regmap::VOICE_REVERB_EN_HIGH => shadow, 167 | regmap::VOICE_STATUS_LOW => shadow, 168 | regmap::VOICE_STATUS_HIGH => shadow, 169 | regmap::TRANSFER_START_INDEX => shadow, 170 | regmap::CONTROL => shadow, 171 | regmap::TRANSFER_CONTROL => shadow, 172 | regmap::STATUS => self.status(), 173 | regmap::CD_VOLUME_LEFT => shadow, 174 | regmap::CD_VOLUME_RIGHT => shadow, 175 | regmap::EXT_VOLUME_LEFT => shadow, 176 | regmap::EXT_VOLUME_RIGHT => shadow, 177 | regmap::CURRENT_VOLUME_LEFT => 178 | // XXX return current value 179 | shadow, 180 | regmap::CURRENT_VOLUME_RIGHT => 181 | // XXX return current value 182 | shadow, 183 | _ => panic!("Unhandled SPU load {:x}", offset), 184 | } 185 | }; 186 | 187 | r as u32 188 | } 189 | 190 | fn control(&self) -> u16 { 191 | self.shadow_registers[regmap::CONTROL] 192 | } 193 | 194 | fn set_control(&mut self, ctrl: u16) { 195 | // XXX if a game enables the SPU IRQ we're probably going to 196 | // be in trouble 197 | if ctrl & 0x40 != 0 { 198 | panic!("Unhandled SPU IRQ"); 199 | } 200 | } 201 | 202 | fn status(&self) -> u16 { 203 | self.control() & 0x3f 204 | } 205 | 206 | /// Set the SPU RAM access pattern 207 | fn set_transfer_control(&self, val: u16) { 208 | // For now only support "normal" (i.e. sequential) access 209 | if val != 0x4 { 210 | panic!("Unhandled SPU RAM access pattern {:x}", val); 211 | } 212 | } 213 | 214 | fn fifo_write(&mut self, val: u16) { 215 | // XXX handle FIFO overflow? 216 | let index = self.ram_index; 217 | 218 | debug!("SPU RAM store {:05x}: {:04x}", index, val); 219 | 220 | self.ram[index as usize] = val; 221 | self.ram_index = (index + 1) & 0x3ffff; 222 | } 223 | } 224 | 225 | impl Encodable for Spu { 226 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 227 | s.emit_struct("Spu", 3, |s| { 228 | try!(s.emit_struct_field( 229 | "shadow_registers", 0, 230 | |s| s.emit_seq( 231 | 0x100, 232 | |s| { 233 | for i in 0..0x100 { 234 | try!(s.emit_seq_elt( 235 | i, 236 | |s| self.shadow_registers[i].encode(s))); 237 | } 238 | 239 | Ok(()) 240 | }))); 241 | 242 | try!(s.emit_struct_field( 243 | "ram", 1, 244 | |s| s.emit_seq( 245 | 256 * 1024, 246 | |s| { 247 | for i in 0..(256 * 1024) { 248 | try!(s.emit_seq_elt( 249 | i, 250 | |s| self.ram[i].encode(s))); 251 | } 252 | 253 | Ok(()) 254 | }))); 255 | 256 | try!(s.emit_struct_field("ram_index", 2, 257 | |s| self.ram_index.encode(s))); 258 | 259 | Ok(()) 260 | }) 261 | } 262 | } 263 | 264 | impl Decodable for Spu { 265 | fn decode(d: &mut D) -> Result { 266 | d.read_struct("Spu", 3, |d| { 267 | let mut spu = Spu::new(); 268 | 269 | try!(d.read_struct_field( 270 | "shadow_registers", 0, 271 | |d| { 272 | d.read_seq(|d, len| { 273 | if len != 0x100 { 274 | return Err( 275 | d.error("wrong SPU shadow registers length")); 276 | } 277 | 278 | for i in 0..len { 279 | spu.shadow_registers[i] = 280 | try!(d.read_seq_elt(i, Decodable::decode)); 281 | } 282 | 283 | Ok(()) 284 | }) 285 | })); 286 | 287 | 288 | try!(d.read_struct_field( 289 | "ram", 1, 290 | |d| { 291 | d.read_seq(|d, len| { 292 | if len != 256 * 1024 { 293 | return Err( 294 | d.error("wrong SPU RAM length")); 295 | } 296 | 297 | for i in 0..len { 298 | spu.ram[i] = 299 | try!(d.read_seq_elt(i, Decodable::decode)); 300 | } 301 | 302 | Ok(()) 303 | }) 304 | })); 305 | 306 | spu.ram_index = 307 | try!(d.read_struct_field("ram_index", 308 | 2, 309 | Decodable::decode)); 310 | 311 | Ok(spu) 312 | }) 313 | } 314 | } 315 | 316 | mod regmap { 317 | //! SPU register map: offset from the base in number of 318 | //! *halfwords* 319 | 320 | pub mod voice { 321 | //! Per-voice regmap, repeated 24 times 322 | pub const VOLUME_LEFT: usize = 0x0; 323 | pub const VOLUME_RIGHT: usize = 0x1; 324 | pub const ADPCM_SAMPLE_RATE: usize = 0x2; 325 | pub const ADPCM_START_INDEX: usize = 0x3; 326 | pub const ADPCM_ADSR_LOW: usize = 0x4; 327 | pub const ADPCM_ADSR_HIGH: usize = 0x5; 328 | pub const CURRENT_ADSR_VOLUME: usize = 0x6; 329 | pub const ADPCM_REPEAT_INDEX: usize = 0x7; 330 | } 331 | 332 | pub const MAIN_VOLUME_LEFT: usize = 0xc0; 333 | pub const MAIN_VOLUME_RIGHT: usize = 0xc1; 334 | pub const REVERB_VOLUME_LEFT: usize = 0xc2; 335 | pub const REVERB_VOLUME_RIGHT: usize = 0xc3; 336 | pub const VOICE_ON_LOW: usize = 0xc4; 337 | pub const VOICE_ON_HIGH: usize = 0xc5; 338 | pub const VOICE_OFF_LOW: usize = 0xc6; 339 | pub const VOICE_OFF_HIGH: usize = 0xc7; 340 | pub const VOICE_PITCH_MOD_EN_LOW: usize = 0xc8; 341 | pub const VOICE_PITCH_MOD_EN_HIGH: usize = 0xc9; 342 | pub const VOICE_NOISE_EN_LOW: usize = 0xca; 343 | pub const VOICE_NOISE_EN_HIGH: usize = 0xcb; 344 | pub const VOICE_REVERB_EN_LOW: usize = 0xcc; 345 | pub const VOICE_REVERB_EN_HIGH: usize = 0xcd; 346 | pub const VOICE_STATUS_LOW: usize = 0xce; 347 | pub const VOICE_STATUS_HIGH: usize = 0xcf; 348 | 349 | pub const REVERB_BASE: usize = 0xd1; 350 | pub const TRANSFER_START_INDEX: usize = 0xd3; 351 | pub const TRANSFER_FIFO: usize = 0xd4; 352 | pub const CONTROL: usize = 0xd5; 353 | pub const TRANSFER_CONTROL: usize = 0xd6; 354 | pub const STATUS: usize = 0xd7; 355 | pub const CD_VOLUME_LEFT: usize = 0xd8; 356 | pub const CD_VOLUME_RIGHT: usize = 0xd9; 357 | pub const EXT_VOLUME_LEFT: usize = 0xda; 358 | pub const EXT_VOLUME_RIGHT: usize = 0xdb; 359 | pub const CURRENT_VOLUME_LEFT: usize = 0xdc; 360 | pub const CURRENT_VOLUME_RIGHT: usize = 0xdd; 361 | 362 | pub const REVERB_APF_OFFSET1: usize = 0xe0; 363 | pub const REVERB_APF_OFFSET2: usize = 0xe1; 364 | pub const REVERB_REFLECT_VOLUME1: usize = 0xe2; 365 | pub const REVERB_COMB_VOLUME1: usize = 0xe3; 366 | pub const REVERB_COMB_VOLUME2: usize = 0xe4; 367 | pub const REVERB_COMB_VOLUME3: usize = 0xe5; 368 | pub const REVERB_COMB_VOLUME4: usize = 0xe6; 369 | pub const REVERB_REFLECT_VOLUME2: usize = 0xe7; 370 | pub const REVERB_APF_VOLUME1: usize = 0xe8; 371 | pub const REVERB_APF_VOLUME2: usize = 0xe9; 372 | pub const REVERB_REFLECT_SAME_LEFT1: usize = 0xea; 373 | pub const REVERB_REFLECT_SAME_RIGHT1: usize = 0xeb; 374 | pub const REVERB_COMB_LEFT1: usize = 0xec; 375 | pub const REVERB_COMB_RIGHT1: usize = 0xed; 376 | pub const REVERB_COMB_LEFT2: usize = 0xee; 377 | pub const REVERB_COMB_RIGHT2: usize = 0xef; 378 | pub const REVERB_REFLECT_SAME_LEFT2: usize = 0xf0; 379 | pub const REVERB_REFLECT_SAME_RIGHT2: usize = 0xf1; 380 | pub const REVERB_REFLECT_DIFF_LEFT1: usize = 0xf2; 381 | pub const REVERB_REFLECT_DIFF_RIGHT1: usize = 0xf3; 382 | pub const REVERB_COMB_LEFT3: usize = 0xf4; 383 | pub const REVERB_COMB_RIGHT3: usize = 0xf5; 384 | pub const REVERB_COMB_LEFT4: usize = 0xf6; 385 | pub const REVERB_COMB_RIGHT4: usize = 0xf7; 386 | pub const REVERB_REFLECT_DIFF_LEFT2: usize = 0xf8; 387 | pub const REVERB_REFLECT_DIFF_RIGHT2: usize = 0xf9; 388 | pub const REVERB_APF_LEFT1: usize = 0xfa; 389 | pub const REVERB_APF_RIGHT1: usize = 0xfb; 390 | pub const REVERB_APF_LEFT2: usize = 0xfc; 391 | pub const REVERB_APF_RIGHT2: usize = 0xfd; 392 | pub const REVERB_INPUT_VOLUME_LEFT: usize = 0xfe; 393 | pub const REVERB_INPUT_VOLUME_RIGHT: usize = 0xff; 394 | } 395 | -------------------------------------------------------------------------------- /src/memory/timers.rs: -------------------------------------------------------------------------------- 1 | use timekeeper::{Cycles, FracCycles, Peripheral}; 2 | use gpu::Gpu; 3 | use super::Addressable; 4 | use interrupt::Interrupt; 5 | use shared::SharedState; 6 | 7 | #[derive(Debug, RustcDecodable, RustcEncodable)] 8 | pub struct Timers { 9 | /// The three timers. They're mostly identical except that they 10 | /// can each select a unique clock source besides the regular 11 | /// system clock: 12 | /// 13 | /// * Timer 0: GPU pixel clock 14 | /// * Timer 1: GPU horizontal blanking 15 | /// * Timer 2: System clock / 8 16 | /// The also each have a 17 | timers: [Timer; 3], 18 | } 19 | 20 | impl Timers { 21 | pub fn new() -> Timers { 22 | Timers { 23 | timers: [Timer::new(Peripheral::Timer0), 24 | Timer::new(Peripheral::Timer1), 25 | Timer::new(Peripheral::Timer2), 26 | ], 27 | } 28 | } 29 | 30 | pub fn store(&mut self, 31 | shared: &mut SharedState, 32 | gpu: &mut Gpu, 33 | offset: u32, 34 | val: u32) { 35 | 36 | if T::size() == 1 { 37 | panic!("Unhandled byte timer store"); 38 | } 39 | 40 | let val = val as u16; 41 | 42 | let instance = offset >> 4; 43 | 44 | let timer = &mut self.timers[instance as usize]; 45 | 46 | timer.sync(shared); 47 | 48 | match offset & 0xf { 49 | 0 => timer.set_counter(val), 50 | 4 => timer.set_mode(val), 51 | 8 => timer.set_target(val), 52 | n => panic!("Unhandled timer register {}", n), 53 | } 54 | 55 | if timer.needs_gpu() { 56 | gpu.sync(shared); 57 | } 58 | 59 | timer.reconfigure(shared, gpu); 60 | } 61 | 62 | pub fn load(&mut self, 63 | shared: &mut SharedState, 64 | offset: u32) -> u32 { 65 | 66 | if T::size() == 1 { 67 | panic!("Unhandled byte timer load"); 68 | } 69 | 70 | let instance = offset >> 4; 71 | 72 | let timer = &mut self.timers[instance as usize]; 73 | 74 | timer.sync(shared); 75 | 76 | let val = match offset & 0xf { 77 | 0 => timer.counter(), 78 | 4 => timer.mode(), 79 | 8 => timer.target(), 80 | n => panic!("Unhandled timer register {}", n), 81 | }; 82 | 83 | val as u32 84 | } 85 | 86 | /// Called by the GPU when the video timings change since it can 87 | /// affect the timers that use them. 88 | pub fn video_timings_changed(&mut self, 89 | shared: &mut SharedState, 90 | gpu: &Gpu) { 91 | 92 | for t in &mut self.timers { 93 | if t.needs_gpu() { 94 | t.sync(shared); 95 | t.reconfigure(shared, gpu); 96 | } 97 | } 98 | } 99 | 100 | pub fn sync(&mut self, shared: &mut SharedState) { 101 | 102 | if shared.tk().needs_sync(Peripheral::Timer0) { 103 | self.timers[0].sync(shared); 104 | } 105 | 106 | if shared.tk().needs_sync(Peripheral::Timer1) { 107 | self.timers[1].sync(shared); 108 | } 109 | 110 | if shared.tk().needs_sync(Peripheral::Timer2) { 111 | self.timers[2].sync(shared); 112 | } 113 | } 114 | } 115 | 116 | #[derive(Debug, RustcDecodable, RustcEncodable)] 117 | struct Timer { 118 | /// Timer instance (Timer0, 1 or 2) 119 | instance: Peripheral, 120 | /// Counter value 121 | counter: u16, 122 | /// Counter target 123 | target: u16, 124 | /// If true we synchronize the timer with an external signal 125 | use_sync: bool, 126 | /// The synchronization mode when `free_run` is false. Each one of 127 | /// the three timers interprets this mode differently. 128 | sync: Sync, 129 | /// If true the counter is reset when it reaches the `target` 130 | /// value. Otherwise let it count all the way to `0xffff` and wrap 131 | /// around. 132 | target_wrap: bool, 133 | /// Raise interrupt when the counter reaches the `target` 134 | target_irq: bool, 135 | /// Raise interrupt when the counter passes 0xffff and wraps 136 | /// around 137 | wrap_irq: bool, 138 | /// If true the interrupt is automatically cleared and will 139 | /// re-trigger when one of the interrupt conditions occurs again 140 | repeat_irq: bool, 141 | /// If true the irq signal is inverted each time an interrupt 142 | /// condition is reached instead of a single pulse. 143 | negate_irq: bool, 144 | /// Clock source (2bits). Each timer can either use the CPU 145 | /// SysClock or an alternative clock source. 146 | clock_source: ClockSource, 147 | /// True if the target has been reached since the last read 148 | target_reached: bool, 149 | /// True if the counter reached 0xffff and overflowed since the 150 | /// last read 151 | overflow_reached: bool, 152 | /// Period of a counter tick. Stored as a fractional cycle count 153 | /// since the GPU can be used as a source. 154 | period: FracCycles, 155 | /// Current position within a period of a counter tick. 156 | phase: FracCycles, 157 | /// True if interrupt signal is active 158 | interrupt: bool, 159 | } 160 | 161 | impl Timer { 162 | fn new(instance: Peripheral) -> Timer { 163 | Timer { 164 | instance: instance, 165 | counter: 0, 166 | target: 0, 167 | use_sync: false, 168 | sync: Sync::from_field(0), 169 | target_wrap: false, 170 | target_irq: false, 171 | wrap_irq: false, 172 | repeat_irq: false, 173 | negate_irq: false, 174 | clock_source: ClockSource::from_field(0), 175 | target_reached: false, 176 | overflow_reached: false, 177 | period: FracCycles::from_cycles(1), 178 | phase: FracCycles::from_cycles(0), 179 | interrupt: false, 180 | } 181 | } 182 | 183 | /// Recomputes the entire timer's internal state. Must be called 184 | /// when the timer's config changes *or* when the timer relies on 185 | /// the GPU's video timings and those timings change. 186 | /// 187 | /// If the GPU is needed for the timings it must be synchronized 188 | /// before this function is called. 189 | fn reconfigure(&mut self, shared: &mut SharedState, gpu: &Gpu) { 190 | 191 | match self.clock_source.clock(self.instance) { 192 | Clock::SysClock => { 193 | self.period = FracCycles::from_cycles(1); 194 | self.phase = FracCycles::from_cycles(0); 195 | }, 196 | Clock::SysClockDiv8 => { 197 | self.period = FracCycles::from_cycles(8); 198 | // XXX When does the divider get reset exactly? 199 | // Maybe it's running continuously? 200 | self.phase = FracCycles::from_cycles(0); 201 | }, 202 | Clock::GpuDotClock => { 203 | self.period = gpu.dotclock_period(); 204 | self.phase = gpu.dotclock_phase(); 205 | }, 206 | Clock::GpuHSync => { 207 | self.period = gpu.hsync_period(); 208 | self.phase = gpu.hsync_phase(); 209 | } 210 | } 211 | 212 | self.predict_next_sync(shared); 213 | } 214 | 215 | /// Synchronize this timer. 216 | fn sync(&mut self, 217 | shared: &mut SharedState) { 218 | 219 | let delta = shared.tk().sync(self.instance); 220 | 221 | if delta == 0 { 222 | // The interrupt code below might glitch if it's called 223 | // two times in a row (trigger the interrupt twice). It 224 | // probably wouldn't be too dangerous but I'd rather keep it clean. 225 | return; 226 | } 227 | 228 | let delta_frac = FracCycles::from_cycles(delta); 229 | 230 | let ticks = delta_frac.add(self.phase); 231 | 232 | let mut count = ticks.get_fp() / self.period.get_fp(); 233 | let phase = ticks.get_fp() % self.period.get_fp(); 234 | 235 | // Store the new phase 236 | self.phase = FracCycles::from_fp(phase); 237 | 238 | count += self.counter as Cycles; 239 | 240 | let mut target_passed = false; 241 | 242 | if (self.counter <= self.target) && (count > self.target as Cycles) { 243 | // XXX I'm not sure if those flags are set when the 244 | // target/0xffff are reached or at the beginning of the 245 | // next period. 246 | self.target_reached = true; 247 | target_passed = true; 248 | } 249 | 250 | let wrap = match self.target_wrap { 251 | // We wrap *after* the target is reached, so we need to 252 | // add 1 to it for our modulo to work correctly later. 253 | 254 | // XXX: Actually it seems that it happens after the target 255 | // is reach but not a full period later. Maybe only one 256 | // cycle? This IP is a mess. 257 | true => (self.target as Cycles) + 1, 258 | false => 0x10000, 259 | }; 260 | 261 | let mut overflow = false; 262 | 263 | if count >= wrap { 264 | count %= wrap; 265 | 266 | // XXX check that this flag is set even when we're using 267 | // `target_wrap` and target is set to 0xffff or if it's 268 | // just in "targetless" mode. 269 | if wrap == 0x10000 { 270 | self.overflow_reached = true; 271 | // I can't reuse `self.overflow_reached` since it 272 | // might be set continuously if the software doesn't 273 | // ack it by reading the mode register 274 | overflow = true; 275 | } 276 | } 277 | 278 | self.counter = count as u16; 279 | if (self.wrap_irq && overflow) || (self.target_irq && target_passed) { 280 | let interrupt = 281 | match self.instance { 282 | Peripheral::Timer0 => Interrupt::Timer0, 283 | Peripheral::Timer1 => Interrupt::Timer1, 284 | Peripheral::Timer2 => Interrupt::Timer2, 285 | _ => unreachable!(), 286 | }; 287 | 288 | if self.negate_irq { 289 | panic!("Unhandled negate IRQ!"); 290 | } else { 291 | // Pulse interrupt 292 | shared.irq_state_mut().assert(interrupt); 293 | self.interrupt = true; 294 | } 295 | } else if !self.negate_irq { 296 | // Pulse is over 297 | self.interrupt = false; 298 | } 299 | 300 | self.predict_next_sync(shared) 301 | } 302 | 303 | fn predict_next_sync(&mut self, shared: &mut SharedState) { 304 | // XXX add support for wrap IRQ 305 | 306 | if !self.target_irq { 307 | // No IRQ enabled, we don't need to be called back. 308 | shared.tk().no_sync_needed(self.instance); 309 | return; 310 | } 311 | 312 | let countdown = 313 | if self.counter <= self.target { 314 | self.target - self.counter 315 | } else { 316 | 0xffff - self.counter + self.target 317 | }; 318 | 319 | // Convert from timer count to CPU cycles. I add + 1 to the 320 | // countdown because the interrupt is generated on the 321 | // following cycle (I think?) 322 | let mut delta = self.period.get_fp() * (countdown as Cycles + 1); 323 | delta -= self.phase.get_fp(); 324 | 325 | // Round up to the next CPU cycle 326 | let delta = FracCycles::from_fp(delta).ceil(); 327 | 328 | shared.tk().set_next_sync_delta(self.instance, delta); 329 | } 330 | 331 | /// Return true if the timer relies on the GPU for the clock 332 | /// source or synchronization 333 | pub fn needs_gpu(&self) -> bool { 334 | if self.use_sync { 335 | warn!("Sync mode not supported!"); 336 | } 337 | 338 | self.clock_source.clock(self.instance).needs_gpu() 339 | } 340 | 341 | fn mode(&mut self) -> u16 { 342 | let mut r = 0u16; 343 | 344 | r |= self.use_sync as u16; 345 | r |= (self.sync as u16) << 1; 346 | r |= (self.target_wrap as u16) << 3; 347 | r |= (self.target_irq as u16) << 4; 348 | r |= (self.wrap_irq as u16) << 5; 349 | r |= (self.repeat_irq as u16) << 6; 350 | r |= (self.negate_irq as u16) << 7; 351 | r |= (self.clock_source.0 as u16) << 8; 352 | // Interrupt field is active low 353 | r |= ((!self.interrupt) as u16) << 10; 354 | r |= (self.target_reached as u16) << 11; 355 | r |= (self.overflow_reached as u16) << 12; 356 | 357 | // Reading mode resets those flags 358 | self.target_reached = false; 359 | self.overflow_reached = false; 360 | 361 | r 362 | } 363 | 364 | /// Set the value of the mode register 365 | fn set_mode(&mut self, val: u16) { 366 | self.use_sync = (val & 1) != 0; 367 | self.sync = Sync::from_field((val >> 1) & 3); 368 | self.target_wrap = (val >> 3) & 1 != 0; 369 | self.target_irq = (val >> 4) & 1 != 0; 370 | self.wrap_irq = (val >> 5) & 1 != 0; 371 | self.repeat_irq = (val >> 6) & 1 != 0; 372 | self.negate_irq = (val >> 7) & 1 != 0; 373 | self.clock_source = ClockSource::from_field((val >> 8) & 3); 374 | 375 | // Writing to mode resets the interrupt flag 376 | self.interrupt = false; 377 | 378 | // Writing to mode resets the counter 379 | self.counter = 0; 380 | 381 | if self.wrap_irq { 382 | panic!("Wrap IRQ not supported"); 383 | } 384 | 385 | if (self.wrap_irq || self.target_irq) && !self.repeat_irq { 386 | panic!("One shot timer interrupts are not supported: {:?}", self); 387 | } 388 | 389 | if self.negate_irq { 390 | panic!("Only pulse interrupts are supported: {:?}", self); 391 | } 392 | 393 | if self.use_sync { 394 | warn!("Sync mode is not supported: {:?}", self); 395 | } 396 | } 397 | 398 | fn target(&self) -> u16 { 399 | self.target 400 | } 401 | 402 | fn set_target(&mut self, val: u16) { 403 | self.target = val; 404 | } 405 | 406 | fn counter(&self) -> u16 { 407 | self.counter 408 | } 409 | 410 | fn set_counter(&mut self, val: u16) { 411 | self.counter = val; 412 | } 413 | } 414 | 415 | /// Various synchronization modes when the timer is not in 416 | /// free-run. 417 | #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 418 | enum Sync { 419 | /// For timer 1/2: Pause during H/VBlank. For timer 3: Stop counter 420 | Pause = 0, 421 | /// For timer 1/2: Reset counter at H/VBlank. For timer 3: Free run 422 | Reset = 1, 423 | /// For timer 1/2: Reset counter at H/VBlank and pause outside of 424 | /// it. For timer 3: Free run 425 | ResetAndPause = 2, 426 | /// For timer 1/2: Wait for H/VBlank and then free-run. For timer 427 | /// 3: Stop counter 428 | WaitForSync = 3, 429 | } 430 | 431 | impl Sync { 432 | fn from_field(field: u16) -> Sync { 433 | match field { 434 | 0 => Sync::Pause, 435 | 1 => Sync::Reset, 436 | 2 => Sync::ResetAndPause, 437 | 3 => Sync::WaitForSync, 438 | _ => panic!("Invalid sync mode {}", field), 439 | } 440 | } 441 | } 442 | 443 | /// Clock source is stored on two bits. The meaning of those bits 444 | /// depends on the timer instance. 445 | #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 446 | struct ClockSource(u8); 447 | 448 | impl ClockSource { 449 | fn from_field(field: u16) -> ClockSource { 450 | if (field & !3) != 0 { 451 | panic!("Invalid clock source: {:x}", field); 452 | } 453 | 454 | ClockSource(field as u8) 455 | } 456 | 457 | fn clock(self, instance: Peripheral) -> Clock { 458 | // Annoyingly timers 0 and 1 use values 0 or 2 for the 459 | // sysclock (1 and 3 for the alternative source) while timer 2 460 | // uses 0 and *1* for the sysclock (2 and 3 for the 461 | // alternative source). I don't understand why they needed two 462 | // bits to begin with, they could at least have made the 463 | // encoding consistent. Maybe there's more to it than that? 464 | let lookup = [ 465 | // Timer 0 466 | [ Clock::SysClock, Clock::GpuDotClock, 467 | Clock::SysClock, Clock::GpuDotClock, ], 468 | // Timer 1 469 | [ Clock::SysClock, Clock::GpuHSync, 470 | Clock::SysClock, Clock::GpuHSync, ], 471 | // Timer 2 472 | [ Clock::SysClock, Clock::SysClock, 473 | Clock::SysClockDiv8, Clock::SysClockDiv8, ], 474 | ]; 475 | 476 | let source = self.0 as usize; 477 | 478 | match instance { 479 | Peripheral::Timer0 => lookup[0][source], 480 | Peripheral::Timer1 => lookup[1][source], 481 | Peripheral::Timer2 => lookup[2][source], 482 | _ => unreachable!(), 483 | } 484 | } 485 | } 486 | 487 | 488 | /// The four possible clock sources for the timers 489 | #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 490 | enum Clock { 491 | /// The CPU clock at ~33.87MHz 492 | SysClock, 493 | /// The CPU clock divided by 8 (~4.23MHz) 494 | SysClockDiv8, 495 | /// The GPU's dotclock (depends on hardware, around 53Mhz) 496 | GpuDotClock, 497 | /// The GPU's HSync signal (deponds on hardware and video timings) 498 | GpuHSync, 499 | } 500 | 501 | impl Clock { 502 | /// Returns true if the clock comes from the GPU 503 | fn needs_gpu(self) -> bool { 504 | match self { 505 | Clock::GpuDotClock | Clock::GpuHSync => true, 506 | _ => false, 507 | } 508 | } 509 | } 510 | -------------------------------------------------------------------------------- /src/cpu/tests.rs: -------------------------------------------------------------------------------- 1 | //! This file is automatically generated using psxunittest: 2 | //! https://github.com/daeken/psxunittest 3 | //! 4 | //! /!\ DO NOT EDIT DIRECTLY /!\ 5 | 6 | use gpu::{Gpu, VideoClock}; 7 | use gpu::renderer::{Renderer, PrimitiveAttributes, Vertex}; 8 | use memory::{Interconnect, Addressable}; 9 | use memory; 10 | use shared::SharedState; 11 | use bios::Bios; 12 | 13 | use super::{Cpu, RegisterIndex}; 14 | 15 | /// Dummy GPU renderer to run the tests 16 | struct DummyRenderer; 17 | 18 | impl Renderer for DummyRenderer { 19 | fn set_draw_offset(&mut self, _: i16, _: i16) { 20 | } 21 | 22 | fn set_draw_area(&mut self, _: (u16, u16), _: (u16, u16)) { 23 | } 24 | 25 | fn set_display_mode(&mut self, 26 | _: (u16, u16), 27 | _: (u16, u16), 28 | _: bool) { 29 | } 30 | 31 | fn push_line(&mut self, _: &PrimitiveAttributes, _: &[Vertex; 2]) { 32 | } 33 | 34 | fn push_triangle(&mut self, _: &PrimitiveAttributes, _: &[Vertex; 3]) { 35 | } 36 | 37 | fn push_quad(&mut self, _: &PrimitiveAttributes, _: &[Vertex; 4]) { 38 | } 39 | 40 | fn fill_rect(&mut self, 41 | _: [u8; 3], 42 | _: (u16, u16), 43 | _: (u16, u16)) { 44 | } 45 | 46 | fn load_image(&mut self, 47 | _: (u16, u16), 48 | _: (u16, u16), 49 | _: &[u16]) { 50 | } 51 | } 52 | 53 | fn write_blob(cpu: &mut Cpu, 54 | address: u32, 55 | blob: &[u32]) { 56 | let ram = cpu.interconnect_mut().ram_mut(); 57 | 58 | for (i, &w) in blob.iter().enumerate() { 59 | ram.store::(address + (i * 4) as u32, w); 60 | } 61 | } 62 | 63 | fn write(cpu: &mut Cpu, 64 | address: u32, 65 | v: u32) { 66 | let ram = cpu.interconnect_mut().ram_mut(); 67 | 68 | ram.store::(address, v); 69 | } 70 | 71 | fn read(cpu: &mut Cpu, address: u32) -> u32 { 72 | 73 | let ram = cpu.interconnect().ram(); 74 | 75 | ram.load::(address) 76 | } 77 | 78 | #[test] 79 | fn test_beq() { 80 | let bios = Bios::dummy(); 81 | let gpu = Gpu::new(VideoClock::Ntsc); 82 | let inter = Interconnect::new(bios, gpu, None); 83 | let mut cpu = Cpu::new(inter); 84 | let mut shared = SharedState::new(); 85 | let mut renderer = DummyRenderer; 86 | 87 | for r in 0..31 { 88 | cpu.set_reg(RegisterIndex(r), 0); 89 | } 90 | 91 | cpu.set_reg(RegisterIndex(1), 0x1); 92 | cpu.set_reg(RegisterIndex(2), 0x2); 93 | cpu.set_reg(RegisterIndex(3), -1i32 as u32); 94 | cpu.set_reg(RegisterIndex(4), 0xffffffff); 95 | 96 | write_blob(&mut cpu, 0x80100000, 97 | &[0x10220005, 98 | 0x00000000, 99 | 0x200a0001, 100 | 0x10640004, 101 | 0x00000000, 102 | 0x200b0001, 103 | 0x200a0002, 104 | 0x00000000, 105 | 0x00000000, 106 | 0x0bab6fb8, 107 | 0x00000000]); 108 | 109 | cpu.set_pc(0x80100000); 110 | 111 | let mut timeout = true; 112 | for _ in 0..TIMEOUT { 113 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 114 | timeout = false; 115 | break; 116 | } 117 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 118 | } 119 | assert!(timeout == false); 120 | 121 | assert!(cpu.regs[10] == 0x1); 122 | assert!(cpu.regs[11] == 0); 123 | } 124 | 125 | #[test] 126 | fn test_branch_in_branch_delay() { 127 | let bios = Bios::dummy(); 128 | let gpu = Gpu::new(VideoClock::Ntsc); 129 | let inter = Interconnect::new(bios, gpu, None); 130 | let mut cpu = Cpu::new(inter); 131 | let mut shared = SharedState::new(); 132 | let mut renderer = DummyRenderer; 133 | 134 | for r in 0..31 { 135 | cpu.set_reg(RegisterIndex(r), 0); 136 | } 137 | 138 | 139 | write_blob(&mut cpu, 0x80100000, 140 | &[0x10000002, 141 | 0x10000004, 142 | 0x20030001, 143 | 0x20010001, 144 | 0x10000002, 145 | 0x00000000, 146 | 0x20020001, 147 | 0x00000000, 148 | 0x0bab6fb8, 149 | 0x00000000]); 150 | 151 | cpu.set_pc(0x80100000); 152 | 153 | let mut timeout = true; 154 | for _ in 0..TIMEOUT { 155 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 156 | timeout = false; 157 | break; 158 | } 159 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 160 | } 161 | assert!(timeout == false); 162 | 163 | assert!(cpu.regs[1] == 0x1); 164 | assert!(cpu.regs[2] == 0); 165 | assert!(cpu.regs[3] == 0); 166 | } 167 | 168 | #[test] 169 | fn test_lwr_and_lwr_load_delay() { 170 | let bios = Bios::dummy(); 171 | let gpu = Gpu::new(VideoClock::Ntsc); 172 | let inter = Interconnect::new(bios, gpu, None); 173 | let mut cpu = Cpu::new(inter); 174 | let mut shared = SharedState::new(); 175 | let mut renderer = DummyRenderer; 176 | 177 | for r in 0..31 { 178 | cpu.set_reg(RegisterIndex(r), 0); 179 | } 180 | 181 | write::(&mut cpu, 0, 0x76543210); 182 | write::(&mut cpu, 0x4, 0xfedcba98); 183 | 184 | write_blob(&mut cpu, 0x80100000, 185 | &[0x2401ffff, 186 | 0x98010002, 187 | 0x88010005, 188 | 0x00201021, 189 | 0x2403ffff, 190 | 0x98030002, 191 | 0x00000000, 192 | 0x88030005, 193 | 0x00602021, 194 | 0x2405ffff, 195 | 0x88050005, 196 | 0x00000000, 197 | 0x98050002, 198 | 0x00a03021, 199 | 0x2407ffff, 200 | 0x8c070004, 201 | 0x88070002, 202 | 0x00e04021, 203 | 0x2409ffff, 204 | 0x8c090004, 205 | 0x00000000, 206 | 0x88090002, 207 | 0x01205021, 208 | 0x240bffff, 209 | 0x8c0b0004, 210 | 0x980b0002, 211 | 0x01606021, 212 | 0x240dffff, 213 | 0x8c0d0004, 214 | 0x00000000, 215 | 0x980d0002, 216 | 0x01a07021, 217 | 0x3c0f067e, 218 | 0x35ef067e, 219 | 0x488fc800, 220 | 0x240fffff, 221 | 0x480fc800, 222 | 0x880f0001, 223 | 0x01e08021, 224 | 0x2411ffff, 225 | 0x4811c800, 226 | 0x00000000, 227 | 0x98110001, 228 | 0x02209021, 229 | 0x0bab6fb8, 230 | 0x00000000]); 231 | 232 | cpu.set_pc(0x80100000); 233 | 234 | let mut timeout = true; 235 | for _ in 0..TIMEOUT { 236 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 237 | timeout = false; 238 | break; 239 | } 240 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 241 | } 242 | assert!(timeout == false); 243 | 244 | assert!(cpu.regs[1] == 0xba987654); 245 | assert!(cpu.regs[2] == 0xffffffff); 246 | assert!(cpu.regs[3] == 0xba987654); 247 | assert!(cpu.regs[4] == 0xffff7654); 248 | assert!(cpu.regs[5] == 0xba987654); 249 | assert!(cpu.regs[6] == 0xba98ffff); 250 | assert!(cpu.regs[7] == 0x54321098); 251 | assert!(cpu.regs[8] == 0xffffffff); 252 | assert!(cpu.regs[9] == 0x54321098); 253 | assert!(cpu.regs[10] == 0xfedcba98); 254 | assert!(cpu.regs[11] == 0xfedc7654); 255 | assert!(cpu.regs[12] == 0xffffffff); 256 | assert!(cpu.regs[13] == 0xfedc7654); 257 | assert!(cpu.regs[14] == 0xfedcba98); 258 | assert!(cpu.regs[15] == 0x3210067e); 259 | assert!(cpu.regs[16] == 0xffffffff); 260 | assert!(cpu.regs[17] == 0x6765432); 261 | assert!(cpu.regs[18] == 0x67e067e); 262 | } 263 | 264 | #[test] 265 | fn test_add_1() { 266 | let bios = Bios::dummy(); 267 | let gpu = Gpu::new(VideoClock::Ntsc); 268 | let inter = Interconnect::new(bios, gpu, None); 269 | let mut cpu = Cpu::new(inter); 270 | let mut shared = SharedState::new(); 271 | let mut renderer = DummyRenderer; 272 | 273 | for r in 0..31 { 274 | cpu.set_reg(RegisterIndex(r), 0); 275 | } 276 | 277 | cpu.set_reg(RegisterIndex(1), 0xa); 278 | cpu.set_reg(RegisterIndex(2), -15i32 as u32); 279 | 280 | write_blob(&mut cpu, 0x80100000, 281 | &[0x00201820, 282 | 0x00222020, 283 | 0x00412820, 284 | 0x00423020, 285 | 0x0bab6fb8, 286 | 0x00000000]); 287 | 288 | cpu.set_pc(0x80100000); 289 | 290 | let mut timeout = true; 291 | for _ in 0..TIMEOUT { 292 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 293 | timeout = false; 294 | break; 295 | } 296 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 297 | } 298 | assert!(timeout == false); 299 | 300 | assert!(cpu.regs[1] == 0xa); 301 | assert!(cpu.regs[2] == -15i32 as u32); 302 | assert!(cpu.regs[3] == 0xa); 303 | assert!(cpu.regs[4] == -5i32 as u32); 304 | assert!(cpu.regs[5] == -5i32 as u32); 305 | assert!(cpu.regs[6] == -30i32 as u32); 306 | } 307 | 308 | #[test] 309 | fn test_arithmetic_branching_test() { 310 | let bios = Bios::dummy(); 311 | let gpu = Gpu::new(VideoClock::Ntsc); 312 | let inter = Interconnect::new(bios, gpu, None); 313 | let mut cpu = Cpu::new(inter); 314 | let mut shared = SharedState::new(); 315 | let mut renderer = DummyRenderer; 316 | 317 | for r in 0..31 { 318 | cpu.set_reg(RegisterIndex(r), 0); 319 | } 320 | 321 | cpu.set_reg(RegisterIndex(2), 0xdead); 322 | cpu.set_reg(RegisterIndex(3), 0); 323 | cpu.set_reg(RegisterIndex(5), 0x1); 324 | 325 | write_blob(&mut cpu, 0x80100000, 326 | &[0x00451023, 327 | 0x24630001, 328 | 0x1c40fffd, 329 | 0x00000000, 330 | 0x0bab6fb8, 331 | 0x00000000]); 332 | 333 | cpu.set_pc(0x80100000); 334 | 335 | let mut timeout = true; 336 | for _ in 0..TIMEOUT { 337 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 338 | timeout = false; 339 | break; 340 | } 341 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 342 | } 343 | assert!(timeout == false); 344 | 345 | assert!(cpu.regs[2] == 0); 346 | assert!(cpu.regs[3] == 0xdead); 347 | assert!(cpu.regs[5] == 0x1); 348 | } 349 | 350 | #[test] 351 | fn test_bltzal_and_bgezal() { 352 | let bios = Bios::dummy(); 353 | let gpu = Gpu::new(VideoClock::Ntsc); 354 | let inter = Interconnect::new(bios, gpu, None); 355 | let mut cpu = Cpu::new(inter); 356 | let mut shared = SharedState::new(); 357 | let mut renderer = DummyRenderer; 358 | 359 | for r in 0..31 { 360 | cpu.set_reg(RegisterIndex(r), 0); 361 | } 362 | 363 | 364 | write_blob(&mut cpu, 0x80100000, 365 | &[0x3c05ffff, 366 | 0x34a5ffff, 367 | 0x00000821, 368 | 0x0000f821, 369 | 0x04100002, 370 | 0x00000000, 371 | 0x34010001, 372 | 0x001f102b, 373 | 0x3c03ffff, 374 | 0x3463ffff, 375 | 0x0000f821, 376 | 0x04710002, 377 | 0x00000000, 378 | 0x34030001, 379 | 0x001f202b, 380 | 0x3c05ffff, 381 | 0x34a5ffff, 382 | 0x0000f821, 383 | 0x04b00002, 384 | 0x00000000, 385 | 0x34050001, 386 | 0x001f302b, 387 | 0x00003821, 388 | 0x0000f821, 389 | 0x04110002, 390 | 0x00000000, 391 | 0x34070001, 392 | 0x001f402b, 393 | 0x0bab6fb8, 394 | 0x00000000]); 395 | 396 | cpu.set_pc(0x80100000); 397 | 398 | let mut timeout = true; 399 | for _ in 0..TIMEOUT { 400 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 401 | timeout = false; 402 | break; 403 | } 404 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 405 | } 406 | assert!(timeout == false); 407 | 408 | assert!(cpu.regs[1] == 0x1); 409 | assert!(cpu.regs[2] == 0x1); 410 | assert!(cpu.regs[3] == 0x1); 411 | assert!(cpu.regs[4] == 0x1); 412 | assert!(cpu.regs[5] == -1i32 as u32); 413 | assert!(cpu.regs[6] == 0x1); 414 | assert!(cpu.regs[7] == 0); 415 | assert!(cpu.regs[8] == 0x1); 416 | } 417 | 418 | #[test] 419 | fn test_unaligned_loads() { 420 | let bios = Bios::dummy(); 421 | let gpu = Gpu::new(VideoClock::Ntsc); 422 | let inter = Interconnect::new(bios, gpu, None); 423 | let mut cpu = Cpu::new(inter); 424 | let mut shared = SharedState::new(); 425 | let mut renderer = DummyRenderer; 426 | 427 | for r in 0..31 { 428 | cpu.set_reg(RegisterIndex(r), 0); 429 | } 430 | 431 | write::(&mut cpu, 0xbee0, 0xdeadbeef); 432 | cpu.set_reg(RegisterIndex(30), 0xbee1); 433 | 434 | write_blob(&mut cpu, 0x80100000, 435 | &[0x83c10000, 436 | 0x93c20000, 437 | 0x0bab6fb8, 438 | 0x00000000]); 439 | 440 | cpu.set_pc(0x80100000); 441 | 442 | let mut timeout = true; 443 | for _ in 0..TIMEOUT { 444 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 445 | timeout = false; 446 | break; 447 | } 448 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 449 | } 450 | assert!(timeout == false); 451 | 452 | assert!(cpu.regs[1] == -66i32 as u32); 453 | assert!(cpu.regs[2] == 0xbe); 454 | assert!(cpu.regs[3] == 0); 455 | assert!(cpu.regs[4] == 0); 456 | } 457 | 458 | #[test] 459 | fn test_load_delay_for_cop() { 460 | let bios = Bios::dummy(); 461 | let gpu = Gpu::new(VideoClock::Ntsc); 462 | let inter = Interconnect::new(bios, gpu, None); 463 | let mut cpu = Cpu::new(inter); 464 | let mut shared = SharedState::new(); 465 | let mut renderer = DummyRenderer; 466 | 467 | for r in 0..31 { 468 | cpu.set_reg(RegisterIndex(r), 0); 469 | } 470 | 471 | cpu.set_reg(RegisterIndex(2), 0x80110000); 472 | write::(&mut cpu, 0x80110000, 0xdeadbeef); 473 | 474 | write_blob(&mut cpu, 0x80100000, 475 | &[0x8c430000, 476 | 0x00000000, 477 | 0x4803c800, 478 | 0x10600004, 479 | 0x00000000, 480 | 0x20010001, 481 | 0x0804000a, 482 | 0x00000000, 483 | 0x20010002, 484 | 0x0804000a, 485 | 0x0bab6fb8, 486 | 0x00000000]); 487 | 488 | cpu.set_pc(0x80100000); 489 | 490 | let mut timeout = true; 491 | for _ in 0..TIMEOUT { 492 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 493 | timeout = false; 494 | break; 495 | } 496 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 497 | } 498 | assert!(timeout == false); 499 | 500 | assert!(cpu.regs[3] == 0); 501 | assert!(cpu.regs[1] == 0x1); 502 | } 503 | 504 | #[test] 505 | fn test_swl_and_swr() { 506 | let bios = Bios::dummy(); 507 | let gpu = Gpu::new(VideoClock::Ntsc); 508 | let inter = Interconnect::new(bios, gpu, None); 509 | let mut cpu = Cpu::new(inter); 510 | let mut shared = SharedState::new(); 511 | let mut renderer = DummyRenderer; 512 | 513 | for r in 0..31 { 514 | cpu.set_reg(RegisterIndex(r), 0); 515 | } 516 | 517 | cpu.set_reg(RegisterIndex(1), 0); 518 | cpu.set_reg(RegisterIndex(2), 0x76543210); 519 | cpu.set_reg(RegisterIndex(3), 0xfedcba98); 520 | 521 | write_blob(&mut cpu, 0x80100000, 522 | &[0xac220000, 523 | 0xa8230000, 524 | 0x24210004, 525 | 0xac220000, 526 | 0xa8230001, 527 | 0x24210004, 528 | 0xac220000, 529 | 0xa8230002, 530 | 0x24210004, 531 | 0xac220000, 532 | 0xa8230003, 533 | 0x24210004, 534 | 0xac220000, 535 | 0xb8230000, 536 | 0x24210004, 537 | 0xac220000, 538 | 0xb8230001, 539 | 0x24210004, 540 | 0xac220000, 541 | 0xb8230002, 542 | 0x24210004, 543 | 0xac220000, 544 | 0xb8230003, 545 | 0x0bab6fb8, 546 | 0x00000000]); 547 | 548 | cpu.set_pc(0x80100000); 549 | 550 | let mut timeout = true; 551 | for _ in 0..TIMEOUT { 552 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 553 | timeout = false; 554 | break; 555 | } 556 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 557 | } 558 | assert!(timeout == false); 559 | 560 | assert!(read::(&mut cpu, 0) == 0x765432fe); 561 | assert!(read::(&mut cpu, 0x4) == 0x7654fedc); 562 | assert!(read::(&mut cpu, 0x8) == 0x76fedcba); 563 | assert!(read::(&mut cpu, 0xc) == 0xfedcba98); 564 | assert!(read::(&mut cpu, 0x10) == 0xfedcba98); 565 | assert!(read::(&mut cpu, 0x14) == 0xdcba9810); 566 | assert!(read::(&mut cpu, 0x18) == 0xba983210); 567 | assert!(read::(&mut cpu, 0x1c) == 0x98543210); 568 | } 569 | 570 | #[test] 571 | fn test_multiple_load_cancelling() { 572 | let bios = Bios::dummy(); 573 | let gpu = Gpu::new(VideoClock::Ntsc); 574 | let inter = Interconnect::new(bios, gpu, None); 575 | let mut cpu = Cpu::new(inter); 576 | let mut shared = SharedState::new(); 577 | let mut renderer = DummyRenderer; 578 | 579 | for r in 0..31 { 580 | cpu.set_reg(RegisterIndex(r), 0); 581 | } 582 | 583 | write::(&mut cpu, 0, 0x7001a7e); 584 | cpu.set_reg(RegisterIndex(1), 0x600dc0de); 585 | 586 | write_blob(&mut cpu, 0x80100000, 587 | &[0x40016000, 588 | 0x8c010000, 589 | 0x40017800, 590 | 0x8c010000, 591 | 0x8c010000, 592 | 0x00201021, 593 | 0x0bab6fb8, 594 | 0x00000000]); 595 | 596 | cpu.set_pc(0x80100000); 597 | 598 | let mut timeout = true; 599 | for _ in 0..TIMEOUT { 600 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 601 | timeout = false; 602 | break; 603 | } 604 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 605 | } 606 | assert!(timeout == false); 607 | 608 | assert!(cpu.regs[1] == 0x7001a7e); 609 | assert!(cpu.regs[2] == 0x600dc0de); 610 | } 611 | 612 | #[test] 613 | fn test_lwl_and_lwr() { 614 | let bios = Bios::dummy(); 615 | let gpu = Gpu::new(VideoClock::Ntsc); 616 | let inter = Interconnect::new(bios, gpu, None); 617 | let mut cpu = Cpu::new(inter); 618 | let mut shared = SharedState::new(); 619 | let mut renderer = DummyRenderer; 620 | 621 | for r in 0..31 { 622 | cpu.set_reg(RegisterIndex(r), 0); 623 | } 624 | 625 | write::(&mut cpu, 0, 0x76543210); 626 | write::(&mut cpu, 0x4, 0xfedcba98); 627 | 628 | write_blob(&mut cpu, 0x80100000, 629 | &[0x98010000, 630 | 0x88010003, 631 | 0x98020001, 632 | 0x88020004, 633 | 0x98030002, 634 | 0x88030005, 635 | 0x98040003, 636 | 0x88040006, 637 | 0x98050004, 638 | 0x88050007, 639 | 0x88060003, 640 | 0x98060000, 641 | 0x88070004, 642 | 0x98070001, 643 | 0x88080005, 644 | 0x98080002, 645 | 0x88090006, 646 | 0x98090003, 647 | 0x880a0007, 648 | 0x980a0004, 649 | 0x240bffff, 650 | 0x880b0000, 651 | 0x240cffff, 652 | 0x980c0000, 653 | 0x240dffff, 654 | 0x880d0001, 655 | 0x240effff, 656 | 0x980e0001, 657 | 0x240fffff, 658 | 0x880f0002, 659 | 0x2410ffff, 660 | 0x98100002, 661 | 0x2411ffff, 662 | 0x88110003, 663 | 0x2412ffff, 664 | 0x98120003, 665 | 0x0bab6fb8, 666 | 0x00000000]); 667 | 668 | cpu.set_pc(0x80100000); 669 | 670 | let mut timeout = true; 671 | for _ in 0..TIMEOUT { 672 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 673 | timeout = false; 674 | break; 675 | } 676 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 677 | } 678 | assert!(timeout == false); 679 | 680 | assert!(cpu.regs[1] == 0x76543210); 681 | assert!(cpu.regs[2] == 0x98765432); 682 | assert!(cpu.regs[3] == 0xba987654); 683 | assert!(cpu.regs[4] == 0xdcba9876); 684 | assert!(cpu.regs[5] == 0xfedcba98); 685 | assert!(cpu.regs[6] == 0x76543210); 686 | assert!(cpu.regs[7] == 0x98765432); 687 | assert!(cpu.regs[8] == 0xba987654); 688 | assert!(cpu.regs[9] == 0xdcba9876); 689 | assert!(cpu.regs[10] == 0xfedcba98); 690 | assert!(cpu.regs[11] == 0x10ffffff); 691 | assert!(cpu.regs[12] == 0x76543210); 692 | assert!(cpu.regs[13] == 0x3210ffff); 693 | assert!(cpu.regs[14] == 0xff765432); 694 | assert!(cpu.regs[15] == 0x543210ff); 695 | assert!(cpu.regs[16] == 0xffff7654); 696 | assert!(cpu.regs[17] == 0x76543210); 697 | assert!(cpu.regs[18] == 0xffffff76); 698 | } 699 | 700 | #[test] 701 | fn test_lh_and_lb_sign_extension() { 702 | let bios = Bios::dummy(); 703 | let gpu = Gpu::new(VideoClock::Ntsc); 704 | let inter = Interconnect::new(bios, gpu, None); 705 | let mut cpu = Cpu::new(inter); 706 | let mut shared = SharedState::new(); 707 | let mut renderer = DummyRenderer; 708 | 709 | for r in 0..31 { 710 | cpu.set_reg(RegisterIndex(r), 0); 711 | } 712 | 713 | write::(&mut cpu, 0, 0x8080); 714 | 715 | write_blob(&mut cpu, 0x80100000, 716 | &[0x84010000, 717 | 0x94020000, 718 | 0x80030000, 719 | 0x90040000, 720 | 0x00000000, 721 | 0x0bab6fb8, 722 | 0x00000000]); 723 | 724 | cpu.set_pc(0x80100000); 725 | 726 | let mut timeout = true; 727 | for _ in 0..TIMEOUT { 728 | if (cpu.pc & 0x0fffffff) == 0xeadbee0 { 729 | timeout = false; 730 | break; 731 | } 732 | cpu.run_next_instruction(&mut (), &mut shared, &mut renderer); 733 | } 734 | assert!(timeout == false); 735 | 736 | assert!(cpu.regs[1] == 0xffff8080); 737 | assert!(cpu.regs[2] == 0x8080); 738 | assert!(cpu.regs[3] == 0xffffff80); 739 | assert!(cpu.regs[4] == 0x80); 740 | } 741 | 742 | /// Number of CPU cycles after which we consider the test to be a 743 | /// failure 744 | const TIMEOUT: usize = 1_000_000; 745 | 746 | --------------------------------------------------------------------------------