├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── cli.yaml ├── screenshots ├── crash2.jpg ├── crash_team_racing.jpg ├── final_fantasy_vii.jpg ├── metal_gear_solid.jpg ├── pacman_world.jpg ├── ridge_racer.jpg ├── silent_hill.jpg ├── spyro.jpg ├── tekken3.jpg └── wu_tang.jpg ├── shaders ├── shader.frag └── shader.vert └── src ├── audio_interface.rs ├── frontend.rs ├── gui.rs ├── main.rs ├── psx ├── adpcm.rs ├── bus.rs ├── cdrom │ ├── container │ │ ├── bin.rs │ │ ├── mod.rs │ │ └── no_disk.rs │ ├── headers.rs │ ├── helpers.rs │ ├── mod.rs │ └── timecode.rs ├── cpu │ ├── cop0.rs │ ├── dmac.rs │ ├── gte.rs │ ├── instruction.rs │ └── mod.rs ├── exp2.rs ├── gpu.rs ├── intc.rs ├── mdec.rs ├── mod.rs ├── rasteriser │ ├── colour.rs │ ├── mod.rs │ ├── vector2i.rs │ └── vector3i.rs ├── sio0 │ ├── controller.rs │ ├── memory_card.rs │ └── mod.rs ├── spu │ ├── adsr.rs │ ├── gauss.rs │ ├── mod.rs │ ├── reverb.rs │ ├── voice.rs │ └── volume.rs ├── timekeeper.rs └── timers.rs ├── queue.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .vscode/ 3 | bios/ 4 | cards/ 5 | games/ 6 | states/ 7 | target/ 8 | *.ini 9 | *.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rpsx" 3 | version = "0.1.0" 4 | authors = ["Kieron Josephs "] 5 | license = "MIT" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | byteorder = "1" 10 | clap = { version = "2", features = ["yaml"] } 11 | gl = "0.14" 12 | imgui = "0.7" 13 | imgui-opengl-renderer = "0.11" 14 | imgui-sdl2 = "0.14.0" 15 | rmp-serde = "1.1.2" 16 | sdl2 = { version = "0.35", features = ["bundled"] } 17 | serde = { version = "1.0", features = ["derive"] } 18 | serde-big-array = "0.5.1" 19 | xz2 = "0.1" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2022 Kieron Josephs. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rpsx 2 | A PlayStation emulator written in Rust as a hobby project. 3 | 4 | Most components of the PlayStation are implemented, and many popular games are playable. 5 | 6 | # Screenshots 7 | 8 |

9 | 10 | 11 |

12 | 13 |

14 | 15 | 16 |

17 | 18 |

19 | 20 | 21 |

22 | 23 |

24 | 25 | 26 |

27 | 28 |

29 | 30 | 31 |

-------------------------------------------------------------------------------- /cli.yaml: -------------------------------------------------------------------------------- 1 | name: rpsx 2 | version: "0.1.0" 3 | author: Kieron Josephs 4 | about: A PlayStation emulator written in Rust 5 | args: 6 | - BIOS: 7 | help: Path to BIOS file 8 | required: true 9 | 10 | - GAME: 11 | help: Path to game file 12 | required: true 13 | -------------------------------------------------------------------------------- /screenshots/crash2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/crash2.jpg -------------------------------------------------------------------------------- /screenshots/crash_team_racing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/crash_team_racing.jpg -------------------------------------------------------------------------------- /screenshots/final_fantasy_vii.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/final_fantasy_vii.jpg -------------------------------------------------------------------------------- /screenshots/metal_gear_solid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/metal_gear_solid.jpg -------------------------------------------------------------------------------- /screenshots/pacman_world.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/pacman_world.jpg -------------------------------------------------------------------------------- /screenshots/ridge_racer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/ridge_racer.jpg -------------------------------------------------------------------------------- /screenshots/silent_hill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/silent_hill.jpg -------------------------------------------------------------------------------- /screenshots/spyro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/spyro.jpg -------------------------------------------------------------------------------- /screenshots/tekken3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/tekken3.jpg -------------------------------------------------------------------------------- /screenshots/wu_tang.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KieronJ/rpsx/2da50a6a8e6208170f0610f7a0c4edc0bf61386d/screenshots/wu_tang.jpg -------------------------------------------------------------------------------- /shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec2 f_tex; 4 | 5 | out vec4 o_col; 6 | 7 | uniform sampler2D t_tex; 8 | 9 | void main() 10 | { 11 | o_col = texture(t_tex, f_tex); 12 | } -------------------------------------------------------------------------------- /shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec2 pos; 4 | in vec2 tex; 5 | 6 | out vec2 f_tex; 7 | 8 | void main() 9 | { 10 | f_tex = tex; 11 | gl_Position = vec4(pos, 0.0, 1.0); 12 | } -------------------------------------------------------------------------------- /src/audio_interface.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::ops::DerefMut; 3 | 4 | use sdl2::audio::{AudioCallback, AudioDevice, AudioSpecDesired}; 5 | 6 | struct AudioBuffer { 7 | data: VecDeque, 8 | } 9 | 10 | impl AudioBuffer { 11 | pub fn new() -> AudioBuffer { 12 | AudioBuffer { 13 | data: VecDeque::new(), 14 | } 15 | } 16 | 17 | pub fn push_samples(&mut self, samples: Vec) { 18 | for sample in samples.iter() { 19 | self.data.push_back(*sample); 20 | } 21 | 22 | while self.data.len() > 512 * 16 { 23 | self.data.pop_front().unwrap(); 24 | } 25 | } 26 | } 27 | 28 | impl AudioCallback for AudioBuffer { 29 | type Channel = i16; 30 | 31 | fn callback(&mut self, out: &mut [i16]) { 32 | let len = self.data.len(); 33 | 34 | let (last_l, last_r) = if len >= 2 { 35 | (self.data[len - 2], self.data[len - 1]) 36 | } else { 37 | (0, 0) 38 | }; 39 | 40 | for i in 0..out.len() { 41 | if let Some(s) = self.data.pop_front() { 42 | out[i] = s; 43 | } else if (i % 2) == 0 { 44 | out[i] = last_l; 45 | } else { 46 | out[i] = last_r; 47 | } 48 | } 49 | } 50 | } 51 | 52 | pub struct AudioInterface { 53 | device: AudioDevice, 54 | } 55 | 56 | impl AudioInterface { 57 | pub fn new(ctx_tmp: &mut sdl2::Sdl, freq: i32, channels: u8, samples: u16) -> AudioInterface { 58 | let audio_subsystem = ctx_tmp.audio().unwrap(); 59 | 60 | let desired_spec = AudioSpecDesired { 61 | freq: Some(freq), 62 | channels: Some(channels), 63 | samples: Some(samples), 64 | }; 65 | 66 | let device = audio_subsystem 67 | .open_playback(None, &desired_spec, |_| AudioBuffer::new()) 68 | .unwrap(); 69 | 70 | AudioInterface { device: device } 71 | } 72 | 73 | pub fn play(&mut self) { 74 | self.device.resume(); 75 | } 76 | 77 | pub fn push_samples(&mut self, samples: Vec) { 78 | self.device.lock().deref_mut().push_samples(samples); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/frontend.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File}; 2 | use std::io::{Read, Write}; 3 | use std::path::Path; 4 | use std::time::Instant; 5 | 6 | use sdl2::controller::{Axis, Button}; 7 | use sdl2::event::{Event, WindowEvent}; 8 | use sdl2::keyboard::Keycode; 9 | 10 | use xz2::read::XzDecoder; 11 | use xz2::write::XzEncoder; 12 | 13 | use crate::{Options, Scaling}; 14 | use crate::psx::System; 15 | use crate::util; 16 | 17 | fn shader_from_source(source: &std::ffi::CStr, kind: gl::types::GLuint) -> Result { 18 | let shader; 19 | 20 | unsafe { 21 | shader = gl::CreateShader(kind); 22 | gl::ShaderSource(shader, 1, &source.as_ptr(), std::ptr::null()); 23 | gl::CompileShader(shader); 24 | } 25 | 26 | Ok(shader) 27 | } 28 | 29 | pub struct Frontend { 30 | window: sdl2::video::Window, 31 | _gl_context: sdl2::video::GLContext, 32 | 33 | event_pump: sdl2::EventPump, 34 | 35 | _controller: Option, 36 | 37 | vao: gl::types::GLuint, 38 | vbo: gl::types::GLuint, 39 | program: gl::types::GLuint, 40 | texture: gl::types::GLuint, 41 | 42 | imgui: imgui::Context, 43 | imgui_sdl2: imgui_sdl2::ImguiSdl2, 44 | imgui_renderer: imgui_opengl_renderer::Renderer, 45 | 46 | last_frame: Instant, 47 | 48 | framebuffer: Box<[u8]>, 49 | } 50 | 51 | impl Frontend { 52 | pub fn create(ctx_temp: &mut sdl2::Sdl, width: u32, height: u32) -> Self { 53 | let video = ctx_temp.video().unwrap(); 54 | let ctr = ctx_temp.game_controller().unwrap(); 55 | 56 | let window = video.window("rpsx", width, height) 57 | .resizable() 58 | .opengl() 59 | .build() 60 | .unwrap(); 61 | let gl_context = window.gl_create_context().unwrap(); 62 | gl::load_with(|s| video.gl_get_proc_address(s) as _); 63 | 64 | let mut vao = 0; 65 | let mut vbo = 0; 66 | let program; 67 | let mut texture = 0; 68 | 69 | let fragment_shader = shader_from_source(&std::ffi::CString::new(include_str!("../shaders/shader.frag")).unwrap(), gl::FRAGMENT_SHADER).unwrap(); 70 | let vertex_shader = shader_from_source(&std::ffi::CString::new(include_str!("../shaders/shader.vert")).unwrap(), gl::VERTEX_SHADER).unwrap(); 71 | 72 | unsafe { 73 | gl::Viewport(0, 0, width as i32, height as i32); 74 | 75 | gl::GenVertexArrays(1, &mut vao); 76 | gl::GenBuffers(1, &mut vbo); 77 | 78 | program = gl::CreateProgram(); 79 | 80 | gl::AttachShader(program, fragment_shader); 81 | gl::AttachShader(program, vertex_shader); 82 | gl::LinkProgram(program); 83 | gl::DetachShader(program, fragment_shader); 84 | gl::DetachShader(program, vertex_shader); 85 | 86 | gl::DeleteShader(fragment_shader); 87 | gl::DeleteShader(vertex_shader); 88 | 89 | gl::GenTextures(1, &mut texture); 90 | gl::BindTexture(gl::TEXTURE_2D, texture); 91 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32); 92 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32); 93 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); 94 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); 95 | 96 | gl::BindBuffer(gl::ARRAY_BUFFER, vbo); 97 | 98 | gl::BindVertexArray(vao); 99 | gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, 16, std::ptr::null()); 100 | gl::VertexAttribPointer(1, 2, gl::FLOAT, gl::FALSE, 16, 8 as _); 101 | } 102 | 103 | let mut imgui = imgui::Context::create(); 104 | let imgui_sdl2 = imgui_sdl2::ImguiSdl2::new(&mut imgui, &window); 105 | let imgui_renderer = imgui_opengl_renderer::Renderer::new(&mut imgui, |s| video.gl_get_proc_address(s) as _); 106 | 107 | let mut controller = None; 108 | 109 | if let Ok(c) = ctr.open(0) { 110 | println!("[FRONTEND] Got connected controller #0 {}", c.name()); 111 | println!("[FRONTEND] Mappings: {}", c.mapping()); 112 | controller = Some(c); 113 | } 114 | 115 | Self { 116 | window: window, 117 | _gl_context: gl_context, 118 | 119 | event_pump: ctx_temp.event_pump().unwrap(), 120 | 121 | _controller: controller, 122 | 123 | vao: vao, 124 | vbo: vbo, 125 | program: program, 126 | texture: texture, 127 | 128 | imgui: imgui, 129 | imgui_sdl2: imgui_sdl2, 130 | imgui_renderer: imgui_renderer, 131 | 132 | last_frame: Instant::now(), 133 | 134 | framebuffer: vec![0; 1024 * 512 * 3].into_boxed_slice(), 135 | } 136 | } 137 | 138 | pub fn update(&mut self, options: &mut Options, system: &mut System) { 139 | for event in self.event_pump.poll_iter() { 140 | self.imgui_sdl2.handle_event(&mut self.imgui, &event); 141 | 142 | if self.imgui_sdl2.ignore_event(&event) { 143 | continue; 144 | } 145 | 146 | match event { 147 | Event::KeyDown { keycode: Some(k), .. } => Frontend::handle_keydown(k, options, system), 148 | Event::KeyUp { keycode: Some(k), .. } => Frontend::handle_keyup(k, options, system), 149 | 150 | Event::ControllerButtonDown { button, .. } => Frontend::handle_controller_button(button, true, system), 151 | Event::ControllerButtonUp { button, .. } => Frontend::handle_controller_button(button, false, system), 152 | Event::ControllerAxisMotion { axis, value, .. } => Frontend::handle_controller_axis(axis, value, system), 153 | 154 | Event::Window { win_event, .. } => { 155 | match win_event { 156 | WindowEvent::Resized(width, height) => { 157 | unsafe { gl::Viewport(0, 0, width, height); } 158 | }, 159 | _ => {}, 160 | }; 161 | }, 162 | Event::Quit { .. } => system.running = false, 163 | _ => {}, 164 | }; 165 | } 166 | 167 | let id = system.get_disc_id(); 168 | let title = format!("rpsx - {} - slot {}", id, options.state_index); 169 | self.window.set_title(&title).expect("unable to set window title"); 170 | } 171 | 172 | fn handle_controller_button(button: Button, down: bool, system: &mut System) { 173 | let controller = system.get_controller(); 174 | 175 | match button { 176 | Button::A => controller.button_cross = down, 177 | Button::B => controller.button_circle = down, 178 | Button::X => controller.button_square = down, 179 | Button::Y => controller.button_triangle = down, 180 | Button::LeftShoulder => controller.button_l1 = down, 181 | Button::RightShoulder => controller.button_r1 = down, 182 | Button::Back => controller.button_select = down, 183 | Button::Start => controller.button_start = down, 184 | Button::LeftStick => controller.button_l3 = down, 185 | Button::RightStick => controller.button_r3 = down, 186 | Button::DPadUp => controller.button_dpad_up = down, 187 | Button::DPadDown => controller.button_dpad_down = down, 188 | Button::DPadLeft => controller.button_dpad_left = down, 189 | Button::DPadRight => controller.button_dpad_right = down, 190 | Button::Guide => { 191 | if !down { 192 | controller.digital_mode ^= true; 193 | println!("[FRONTEND] Digital mode {}", if controller.digital_mode { "enabled" } else { "disabled" }); 194 | } 195 | }, 196 | _ => println!("[FRONTEND] unhandled button {:#?}", button), 197 | } 198 | } 199 | 200 | fn handle_controller_axis(axis: Axis, value: i16, system: &mut System) { 201 | let controller = system.get_controller(); 202 | let normalised = ((value >> 8) + 128) as u8; 203 | 204 | match axis { 205 | Axis::LeftX => controller.axis_lx = normalised, 206 | Axis::LeftY => controller.axis_ly = normalised, 207 | Axis::RightX => controller.axis_rx = normalised, 208 | Axis::RightY => controller.axis_ry = normalised, 209 | Axis::TriggerLeft => controller.button_l2 = normalised >= 192, 210 | Axis::TriggerRight => controller.button_r2 = normalised >= 192, 211 | } 212 | } 213 | 214 | fn handle_keydown(keycode: Keycode, _options: &mut Options, system: &mut System) { 215 | let controller = system.get_controller(); 216 | 217 | match keycode { 218 | Keycode::W => controller.button_dpad_up = true, 219 | Keycode::A => controller.button_dpad_left = true, 220 | Keycode::S => controller.button_dpad_down = true, 221 | Keycode::D => controller.button_dpad_right = true, 222 | Keycode::Q => controller.button_select = true, 223 | Keycode::E => controller.button_start = true, 224 | Keycode::Kp2 => controller.button_cross = true, 225 | Keycode::Kp4 => controller.button_square = true, 226 | Keycode::Kp6 => controller.button_circle = true, 227 | Keycode::Kp8 => controller.button_triangle = true, 228 | Keycode::Num1 => controller.button_l1 = true, 229 | Keycode::Num2 => controller.button_l2 = true, 230 | Keycode::Num3 => controller.button_r1 = true, 231 | Keycode::Num4 => controller.button_r2 = true, 232 | _ => {}, 233 | }; 234 | } 235 | 236 | fn handle_keyup(keycode: Keycode, options: &mut Options, system: &mut System) { 237 | let controller = system.get_controller(); 238 | 239 | match keycode { 240 | Keycode::Tab => options.frame_limit ^= true, 241 | Keycode::F2 => system.reset(), 242 | Keycode::F3 => options.step = true, 243 | Keycode::F4 => { 244 | options.scaling = match options.scaling { 245 | Scaling::None => Scaling::Aspect, 246 | Scaling::Aspect => Scaling::Fullscreen, 247 | Scaling::Fullscreen => Scaling::None 248 | }; 249 | } 250 | Keycode::F6 => Frontend::load_state(system, options.state_index), 251 | Keycode::F7 => Frontend::save_state(system, options.state_index), 252 | Keycode::Comma => { 253 | options.state_index += 1; 254 | options.state_index %= 10; 255 | println!("choosing save slot {}...", options.state_index); 256 | }, 257 | Keycode::F8 => options.draw_full_vram ^= true, 258 | Keycode::F9 => options.crop_overscan ^= true, 259 | Keycode::P => options.pause ^= true, 260 | 261 | Keycode::W => controller.button_dpad_up = false, 262 | Keycode::A => controller.button_dpad_left = false, 263 | Keycode::S => controller.button_dpad_down = false, 264 | Keycode::D => controller.button_dpad_right = false, 265 | Keycode::Q => controller.button_select = false, 266 | Keycode::E => controller.button_start = false, 267 | Keycode::Kp2 => controller.button_cross = false, 268 | Keycode::Kp4 => controller.button_square = false, 269 | Keycode::Kp6 => controller.button_circle = false, 270 | Keycode::Kp8 => controller.button_triangle = false, 271 | Keycode::Num1 => controller.button_l1 = false, 272 | Keycode::Num2 => controller.button_l2 = false, 273 | Keycode::Num3 => controller.button_r1 = false, 274 | Keycode::Num4 => controller.button_r2 = false, 275 | _ => {}, 276 | }; 277 | } 278 | 279 | fn load_state(system: &mut System, index: usize) { 280 | println!("Loading state {}...", index); 281 | 282 | let id = system.get_disc_id_raw(); 283 | let name = format!("./states/{id}_slot{index}.state"); 284 | let path = Path::new(&name); 285 | 286 | if !path.exists() { 287 | println!("No file for save state {}", index); 288 | return; 289 | } 290 | 291 | if let Ok(file) = File::open(path) { 292 | let mut bytes = Vec::new(); 293 | let mut decompressor = XzDecoder::new(file); 294 | decompressor.read_to_end(&mut bytes).unwrap(); 295 | *system = rmp_serde::from_slice(&bytes).unwrap(); 296 | system.reload_host_files(); 297 | system.get_controller().reset_switch_state(); 298 | println!("DONE!"); 299 | } else { 300 | println!("unable to create save state file"); 301 | } 302 | } 303 | 304 | fn save_state(system: &mut System, index: usize) { 305 | println!("Saving state {}...", index); 306 | 307 | let id = system.get_disc_id_raw(); 308 | let name = format!("./states/{id}_slot{index}.state"); 309 | let path = Path::new(&name); 310 | 311 | if let Some(parent) = path.parent() { 312 | fs::create_dir_all(parent).expect("unable to create path to save state file"); 313 | } 314 | 315 | if let Ok(file) = File::create(path) { 316 | let bytes = rmp_serde::to_vec(system).expect("unable to serialize state"); 317 | let mut compressor = XzEncoder::new(file, 6); 318 | compressor.write_all(&bytes).unwrap(); 319 | compressor.finish().unwrap(); 320 | println!("DONE!"); 321 | } else { 322 | println!("Unable to create save state file"); 323 | } 324 | } 325 | 326 | pub fn render(&mut self, options: &Options, system: &System) { 327 | let (width, height) = match options.draw_full_vram { 328 | true => (1024, 512), 329 | false => system.get_display_size(), 330 | }; 331 | 332 | let mut vertices: [[f32; 4]; 4] = [ 333 | [-1.0, 1.0, 0.0, 0.0], 334 | [ 1.0, 1.0, 1.0, 0.0], 335 | [-1.0, -1.0, 0.0, 1.0], 336 | [ 1.0, -1.0, 1.0, 1.0], 337 | ]; 338 | 339 | if !options.draw_full_vram { 340 | let (scale_x, scale_y) = match options.scaling { 341 | Scaling::None => self.calculate_scale_none(), 342 | Scaling::Aspect => self.calculate_scale_aspect(options.crop_overscan), 343 | Scaling::Fullscreen => { 344 | let mut scale = (1.0, 1.0); 345 | 346 | if options.crop_overscan { 347 | scale.1 *= 240.0/216.0; 348 | } 349 | 350 | scale 351 | }, 352 | }; 353 | 354 | vertices[0][0] *= scale_x; 355 | vertices[0][1] *= scale_y; 356 | 357 | vertices[1][0] *= scale_x; 358 | vertices[1][1] *= scale_y; 359 | 360 | vertices[2][0] *= scale_x; 361 | vertices[2][1] *= scale_y; 362 | 363 | vertices[3][0] *= scale_x; 364 | vertices[3][1] *= scale_y; 365 | }; 366 | 367 | system.get_framebuffer(&mut self.framebuffer, options.draw_full_vram); 368 | 369 | unsafe { 370 | gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); 371 | gl::BufferData(gl::ARRAY_BUFFER, std::mem::size_of_val(&vertices) as isize, vertices.as_ptr() as _, gl::DYNAMIC_DRAW); 372 | 373 | gl::BindTexture(gl::TEXTURE_2D, self.texture); 374 | gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB8 as i32, width as i32, height as i32, 0, gl::RGB, gl::UNSIGNED_BYTE, self.framebuffer.as_ptr() as _); 375 | } 376 | 377 | self.imgui_sdl2.prepare_frame(self.imgui.io_mut(), &self.window, &self.event_pump.mouse_state()); 378 | 379 | let now = Instant::now(); 380 | let delta = now - self.last_frame; 381 | self.last_frame = now; 382 | 383 | self.imgui.io_mut().delta_time = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0; 384 | 385 | let ui = self.imgui.frame(); 386 | //ui.show_demo_window(&mut true); 387 | 388 | unsafe { 389 | gl::UseProgram(self.program); 390 | 391 | gl::BindVertexArray(self.vao); 392 | gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); 393 | 394 | gl::EnableVertexAttribArray(0); 395 | gl::EnableVertexAttribArray(1); 396 | 397 | gl::ClearColor(0.0, 0.0, 0.0, 1.0); 398 | gl::Clear(gl::COLOR_BUFFER_BIT); 399 | 400 | gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4); 401 | } 402 | 403 | self.imgui_sdl2.prepare_render(&ui, &self.window); 404 | self.imgui_renderer.render(ui); 405 | 406 | self.window.gl_swap_window(); 407 | } 408 | 409 | fn get_screen_ratio(&self) -> (f32, f32) { 410 | let (window_w, window_h) = self.window.size(); 411 | 412 | let rx = 640.0 / window_w as f32; 413 | let ry = 480.0 / window_h as f32; 414 | 415 | (rx, ry) 416 | } 417 | 418 | fn calculate_scale_none(&self) -> (f32, f32) { 419 | let (x, y) = self.get_screen_ratio(); 420 | (util::clip(x, 0.0, 1.0), util::clip(y, 0.0, 1.0)) 421 | } 422 | 423 | fn calculate_scale_aspect(&self, crop_overscan: bool) -> (f32, f32) { 424 | let (x, y) = self.get_screen_ratio(); 425 | 426 | let scale = if crop_overscan { 427 | 240.0/216.0 428 | } else { 429 | 1.0 430 | }; 431 | 432 | (x / x.max(y / scale), y * scale / x.max(y)) 433 | } 434 | } 435 | 436 | impl Drop for Frontend { 437 | fn drop(&mut self) { 438 | unsafe { 439 | gl::BindVertexArray(0); 440 | gl::BindBuffer(gl::ARRAY_BUFFER, 0); 441 | gl::UseProgram(0); 442 | gl::BindTexture(gl::TEXTURE_2D, 0); 443 | 444 | gl::DeleteVertexArrays(1, &self.vao); 445 | gl::DeleteBuffers(1, &self.vbo); 446 | gl::DeleteProgram(self.program); 447 | gl::DeleteTextures(1, &self.texture); 448 | } 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /src/gui.rs: -------------------------------------------------------------------------------- 1 | use imgui::{ 2 | ImGui, 3 | ImGuiCol, 4 | ImGuiColorEditFlags, 5 | ImGuiCond, 6 | ImString, 7 | ImVec4, 8 | Ui, 9 | }; 10 | 11 | use crate::frontend::Frontend; 12 | use crate::gpu_viewer::{GpuCommand, GpuFrame}; 13 | use crate::psx::System; 14 | use crate::{Options, Scaling}; 15 | 16 | const WHITE: [f32; 3] = [1.0, 1.0, 1.0]; 17 | const RED: [f32; 3] = [1.0, 0.0, 0.0]; 18 | 19 | const RED_OVERLAY: [f32; 4] = [1.0, 0.0, 0.0, 0.25]; 20 | const GREEN_OVERLAY: [f32; 4] = [0.0, 1.0, 0.0, 0.25]; 21 | 22 | pub struct Gui { 23 | pub imgui: ImGui, 24 | pub renderer: Renderer, 25 | } 26 | 27 | impl Gui { 28 | pub fn new(display: &Display) -> Self { 29 | let mut imgui = ImGui::init(); 30 | imgui.set_ini_filename(None); 31 | 32 | let style = imgui.style_mut(); 33 | style.window_rounding = 2.0; 34 | style.child_rounding = 2.0; 35 | style.popup_rounding = 2.0; 36 | style.frame_rounding = 2.0; 37 | style.scrollbar_rounding = 2.0; 38 | style.grab_rounding = 2.0; 39 | 40 | style.colors[ImGuiCol::Border as usize] = ImVec4::new(0.43, 0.43, 0.50, 0.50); 41 | style.colors[ImGuiCol::FrameBg as usize] = ImVec4::new(0.43, 0.43, 0.50, 0.50); 42 | style.colors[ImGuiCol::FrameBgHovered as usize] = ImVec4::new(0.98, 0.37, 0.27, 0.40); 43 | style.colors[ImGuiCol::FrameBgActive as usize] = ImVec4::new(0.98, 0.37, 0.27, 0.67); 44 | style.colors[ImGuiCol::TitleBg as usize] = ImVec4::new(0.04, 0.04, 0.04, 1.00); 45 | style.colors[ImGuiCol::TitleBgActive as usize] = ImVec4::new(0.75, 0.29, 0.21, 1.00); 46 | style.colors[ImGuiCol::TitleBgCollapsed as usize] = ImVec4::new(0.00, 0.00, 0.00, 0.51); 47 | style.colors[ImGuiCol::MenuBarBg as usize] = ImVec4::new(0.14, 0.14, 0.14, 1.00); 48 | style.colors[ImGuiCol::ScrollbarBg as usize] = ImVec4::new(0.02, 0.02, 0.02, 0.53); 49 | style.colors[ImGuiCol::ScrollbarGrab as usize] = ImVec4::new(0.31, 0.31, 0.31, 1.00); 50 | style.colors[ImGuiCol::ScrollbarGrabHovered as usize] = ImVec4::new(0.41, 0.41, 0.41, 1.00); 51 | style.colors[ImGuiCol::ScrollbarGrabActive as usize] = ImVec4::new(0.51, 0.51, 0.51, 1.00); 52 | style.colors[ImGuiCol::CheckMark as usize] = ImVec4::new(0.98, 0.37, 0.27, 1.00); 53 | style.colors[ImGuiCol::SliderGrab as usize] = ImVec4::new(0.88, 0.33, 0.24, 1.00); 54 | style.colors[ImGuiCol::SliderGrabActive as usize] = ImVec4::new(0.98, 0.37, 0.27, 1.00); 55 | style.colors[ImGuiCol::Button as usize] = ImVec4::new(1.00, 0.39, 0.28, 0.40); 56 | style.colors[ImGuiCol::ButtonHovered as usize] = ImVec4::new(0.26, 0.59, 0.98, 1.00); 57 | style.colors[ImGuiCol::ButtonActive as usize] = ImVec4::new(0.06, 0.53, 0.98, 1.00); 58 | style.colors[ImGuiCol::Header as usize] = ImVec4::new(0.26, 0.59, 0.98, 0.31); 59 | style.colors[ImGuiCol::HeaderHovered as usize] = ImVec4::new(0.26, 0.59, 0.98, 0.80); 60 | style.colors[ImGuiCol::HeaderActive as usize] = ImVec4::new(0.26, 0.59, 0.98, 1.00); 61 | style.colors[ImGuiCol::Separator as usize] = ImVec4::new(0.43, 0.43, 0.50, 0.50); 62 | style.colors[ImGuiCol::SeparatorHovered as usize] = ImVec4::new(0.10, 0.40, 0.75, 0.78); 63 | style.colors[ImGuiCol::SeparatorActive as usize] = ImVec4::new(0.10, 0.40, 0.75, 1.00); 64 | style.colors[ImGuiCol::ResizeGrip as usize] = ImVec4::new(0.26, 0.59, 0.98, 0.25); 65 | style.colors[ImGuiCol::ResizeGripHovered as usize] = ImVec4::new(0.26, 0.59, 0.98, 0.67); 66 | style.colors[ImGuiCol::ResizeGripActive as usize] = ImVec4::new(0.26, 0.59, 0.98, 0.95); 67 | style.colors[ImGuiCol::PlotLines as usize] = ImVec4::new(0.61, 0.61, 0.61, 1.00); 68 | style.colors[ImGuiCol::PlotLinesHovered as usize] = ImVec4::new(1.00, 0.43, 0.35, 1.00); 69 | style.colors[ImGuiCol::PlotHistogram as usize] = ImVec4::new(0.90, 0.70, 0.00, 1.00); 70 | style.colors[ImGuiCol::PlotHistogramHovered as usize] = ImVec4::new(1.00, 0.60, 0.00, 1.00); 71 | style.colors[ImGuiCol::TextSelectedBg as usize] = ImVec4::new(0.98, 0.37, 0.27, 0.35); 72 | style.colors[ImGuiCol::DragDropTarget as usize] = ImVec4::new(1.00, 1.00, 0.00, 0.90); 73 | style.colors[ImGuiCol::NavHighlight as usize] = ImVec4::new(0.26, 0.59, 0.98, 1.00); 74 | style.colors[ImGuiCol::NavWindowingHighlight as usize] = ImVec4::new(1.00, 1.00, 1.00, 0.70); 75 | style.colors[ImGuiCol::NavWindowingDimBg as usize] = ImVec4::new(0.80, 0.80, 0.80, 0.20); 76 | style.colors[ImGuiCol::ModalWindowDimBg as usize] = ImVec4::new(0.80, 0.80, 0.80, 0.35); 77 | 78 | let renderer = Renderer::init(&mut imgui, display).unwrap(); 79 | 80 | Self { 81 | imgui: imgui, 82 | renderer: renderer, 83 | } 84 | } 85 | 86 | pub fn draw(ui: &Ui, 87 | options: &mut Options, 88 | gpu_frame: &mut GpuFrame, 89 | system: &mut System, 90 | video: &VideoInterface) { 91 | let file = ui.menu(im_str!("File")); 92 | let emu = ui.menu(im_str!("Emulator")); 93 | let debug = ui.menu(im_str!("Debug")); 94 | let view = ui.menu(im_str!("View")); 95 | 96 | ui.main_menu_bar(|| { 97 | file.build(|| { Gui::draw_file_menu(ui, system, video); }); 98 | emu.build(|| { Gui::draw_emu_menu(ui, options, system); }); 99 | debug.build(|| { Gui::draw_debug_menu(ui, options); }); 100 | view.build(|| { Gui::draw_view_menu(ui, options); }); 101 | 102 | if options.draw_full_vram && options.draw_display_area { 103 | let (window_x, window_y) = video.get_size(); 104 | let (x, y) = system.get_display_origin(); 105 | let (w, h) = system.get_display_size(); 106 | 107 | let x_scale = (window_x as f32) / 1024.0; 108 | let y_scale = (window_y as f32) / 512.0; 109 | 110 | let x1 = (x as f32) * x_scale; 111 | let x2 = ((x + w) as f32) * x_scale; 112 | let y1 = (y as f32) * y_scale; 113 | let y2 = ((y + h) as f32) * y_scale; 114 | 115 | let draw_list = ui.get_window_draw_list(); 116 | 117 | draw_list.with_clip_rect([0.0, 0.0], [window_x as f32, window_y as f32], || { 118 | draw_list.add_line([x1, y1], [x2, y1], RED).build(); 119 | draw_list.add_line([x1, y2], [x2, y2], RED).build(); 120 | draw_list.add_line([x1, y1], [x1, y2], RED).build(); 121 | draw_list.add_line([x2, y1], [x2, y2], RED).build(); 122 | }); 123 | } 124 | }); 125 | 126 | if options.show_gpu_viewer { 127 | Gui::draw_gpu_frame(ui, options, video, gpu_frame); 128 | } 129 | 130 | if options.show_metrics { 131 | ui.show_metrics_window(&mut options.show_metrics); 132 | } 133 | } 134 | 135 | fn draw_gpu_frame(ui: &Ui, 136 | options: &mut Options, 137 | video: &VideoInterface, 138 | gpu_frame: &GpuFrame) { 139 | ui.window(im_str!("GPU Viewer")) 140 | .size((300.0, 395.0), ImGuiCond::Once) 141 | .menu_bar(true) 142 | .build(|| { 143 | ui.menu_bar(|| { 144 | ui.menu(im_str!("Filter")).build(|| {}); 145 | ui.menu(im_str!("Options")).build(|| { 146 | Gui::menu_item(ui, "Overlay position", &mut options.gpu_viewer.overlay_position); 147 | Gui::menu_item(ui, "Overlay texture", &mut options.gpu_viewer.overlay_texture); 148 | Gui::menu_item(ui, "Overlay CLUT", &mut options.gpu_viewer.overlay_clut); 149 | }); 150 | }); 151 | 152 | for i in 0..gpu_frame.commands.len() { 153 | let command = &gpu_frame.commands[i]; 154 | let command_name = GpuCommand::name(command); 155 | let title = ImString::new(format!("{}. {}", i, command_name)); 156 | 157 | Gui::draw_gpu_command(ui, video, title, command); 158 | } 159 | }); 160 | } 161 | 162 | fn draw_gpu_command(ui: &Ui, 163 | video: &VideoInterface, 164 | title: ImString, 165 | command: &GpuCommand) { 166 | if ui.collapsing_header(&title).build() { 167 | match command { 168 | GpuCommand::Polygon(p) => { 169 | if !p.shaded { 170 | let mut colour = p.vertices[0].colour(); 171 | let flags = ImGuiColorEditFlags::NoLabel 172 | | ImGuiColorEditFlags::NoPicker 173 | | ImGuiColorEditFlags::NoOptions 174 | | ImGuiColorEditFlags::NoInputs; 175 | 176 | ui.text("Colour:"); 177 | 178 | ui.same_line(0.0); 179 | 180 | ui.color_edit(im_str!(""), &mut colour) 181 | .flags(flags) 182 | .build(); 183 | } 184 | 185 | let vertices = if p.quad { 4 } else { 3 }; 186 | 187 | for i in 0..vertices { 188 | ui.text(format!("Vertex {}", i + 1)); 189 | 190 | if p.shaded { 191 | ui.same_line(0.0); 192 | 193 | let mut colour = p.vertices[i].colour(); 194 | let flags = ImGuiColorEditFlags::NoLabel 195 | | ImGuiColorEditFlags::NoPicker 196 | | ImGuiColorEditFlags::NoOptions 197 | | ImGuiColorEditFlags::NoInputs; 198 | 199 | ui.color_edit(im_str!(""), &mut colour) 200 | .flags(flags) 201 | .build(); 202 | } 203 | 204 | let (x, y) = p.vertices[i].position; 205 | ui.text(format!("Position: ({}, {})", x, y)); 206 | 207 | if p.textured { 208 | let (u, v) = p.vertices[i].texcoord; 209 | ui.text(format!("Texcoord: ({}, {})", u, v)); 210 | } 211 | 212 | if i < vertices - 1 { 213 | ui.new_line(); 214 | } 215 | } 216 | 217 | let window_size = video.get_size(); 218 | 219 | let p1 = video.to_screen(p.vertices[0].position()); 220 | let p2 = video.to_screen(p.vertices[1].position()); 221 | let p3 = video.to_screen(p.vertices[2].position()); 222 | let p4 = video.to_screen(p.vertices[3].position()); 223 | 224 | let t1 = video.to_screen(p.vertices[0].texcoord(p.texpage)); 225 | let t2 = video.to_screen(p.vertices[1].texcoord(p.texpage)); 226 | let t3 = video.to_screen(p.vertices[2].texcoord(p.texpage)); 227 | let t4 = video.to_screen(p.vertices[3].texcoord(p.texpage)); 228 | 229 | let draw_list = ui.get_window_draw_list(); 230 | draw_list.with_clip_rect((0.0, 0.0), window_size, || { 231 | draw_list.add_triangle(p1, p2, p3, GREEN_OVERLAY) 232 | .filled(true) 233 | .build(); 234 | 235 | if p.textured { 236 | draw_list.add_triangle(t1, t2, t3, RED_OVERLAY) 237 | .filled(true) 238 | .build(); 239 | } 240 | 241 | if p.quad { 242 | draw_list.add_triangle(p2, p3, p4, GREEN_OVERLAY) 243 | .filled(true) 244 | .build(); 245 | 246 | if p.textured { 247 | draw_list.add_triangle(t2, t3, t4, RED_OVERLAY) 248 | .filled(true) 249 | .build(); 250 | } 251 | } 252 | }); 253 | }, 254 | }; 255 | } 256 | } 257 | 258 | fn draw_file_menu(ui: &Ui, system: &System, video: &VideoInterface) { 259 | if Gui::menu_item(ui, "Dump framebuffer", &mut false) { 260 | system.dump_vram(); 261 | video.dump_framebuffer(); 262 | } 263 | } 264 | 265 | fn draw_emu_menu(ui: &Ui, options: &mut Options, system: &mut System) { 266 | if Gui::menu_item_shortcut(ui, "Reset", "F2", &mut false) { 267 | system.reset(); 268 | } 269 | 270 | Gui::menu_item_shortcut(ui, "Step", "F3", &mut options.step); 271 | Gui::menu_item_shortcut(ui, "Pause", "P", &mut options.pause); 272 | Gui::menu_item_shortcut(ui, "Frame limit", "TAB", &mut options.frame_limit); 273 | } 274 | 275 | fn draw_debug_menu(ui: &Ui, options: &mut Options) { 276 | Gui::menu_item(ui, "Draw full VRAM", &mut options.draw_full_vram); 277 | Gui::menu_item(ui, "GPU Viewer", &mut options.show_gpu_viewer); 278 | } 279 | 280 | fn draw_view_menu(ui: &Ui, options: &mut Options) { 281 | Gui::menu_item_enabled(ui, "Draw display area", 282 | options.draw_full_vram, 283 | &mut options.draw_display_area); 284 | 285 | ui.menu(im_str!("Window scaling")).build(|| { 286 | let mut selection = options.scaling as i32; 287 | let items = [ 288 | im_str!("None"), 289 | im_str!("Aspect"), 290 | im_str!("Fullscreen") 291 | ]; 292 | 293 | if ui.combo(im_str!(""), &mut selection, &items, 3) { 294 | options.scaling = Scaling::from(selection); 295 | } 296 | }); 297 | 298 | Gui::menu_item(ui, "Hardware Rendering", &mut options.hardware); 299 | 300 | ui.menu(im_str!("Hardware Scaling")).build(|| { 301 | let mut selection = options.hardware_scale as i32; 302 | let items = [ 303 | im_str!("1x"), 304 | im_str!("2x"), 305 | im_str!("4x"), 306 | im_str!("8x"), 307 | im_str!("16x"), 308 | ]; 309 | 310 | if ui.combo(im_str!(""), &mut selection, &items, 5) { 311 | options.hardware_scale = selection as usize; 312 | } 313 | }); 314 | 315 | Gui::menu_item(ui, "Show metrics", &mut options.show_metrics); 316 | } 317 | 318 | fn menu_item(ui: &Ui, label: &str, selected: &mut bool) -> bool { 319 | let l = ImString::new(label); 320 | ui.menu_item(&l).selected(selected).build() 321 | } 322 | 323 | fn menu_item_shortcut(ui: &Ui, 324 | label: &str, 325 | shortcut: &str, 326 | selected: &mut bool) -> bool { 327 | let l = ImString::new(label); 328 | let s = ImString::new(shortcut); 329 | ui.menu_item(&l).shortcut(&s).selected(selected).build() 330 | } 331 | 332 | fn menu_item_enabled(ui: &Ui, 333 | label: &str, 334 | enabled: bool, 335 | selected: &mut bool) -> bool { 336 | let l = ImString::new(label); 337 | ui.menu_item(&l).enabled(enabled).selected(selected).build() 338 | } 339 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | extern crate imgui; 5 | 6 | mod audio_interface; 7 | mod frontend; 8 | //mod gui; 9 | 10 | mod psx; 11 | pub mod queue; 12 | pub mod util; 13 | 14 | use clap::App; 15 | 16 | use audio_interface::AudioInterface; 17 | use frontend::Frontend; 18 | //use gui::Gui; 19 | 20 | use psx::System; 21 | 22 | #[derive(Clone, Copy)] 23 | pub enum Scaling { 24 | None, 25 | Aspect, 26 | Fullscreen, 27 | } 28 | 29 | impl Scaling { 30 | pub fn from(value: i32) -> Scaling { 31 | use Scaling::*; 32 | 33 | match value { 34 | 0 => None, 35 | 1 => Aspect, 36 | 2 => Fullscreen, 37 | _ => panic!(), 38 | } 39 | } 40 | } 41 | 42 | pub struct Options { 43 | draw_full_vram: bool, 44 | scaling: Scaling, 45 | crop_overscan: bool, 46 | 47 | pause: bool, 48 | step: bool, 49 | 50 | frame_limit: bool, 51 | 52 | state_index: usize, 53 | } 54 | 55 | fn main() { 56 | let yaml = load_yaml!("../cli.yaml"); 57 | let matches = App::from_yaml(yaml).get_matches(); 58 | 59 | let bios_filepath = matches.value_of("BIOS").unwrap(); 60 | let game_filepath = matches.value_of("GAME").unwrap(); 61 | 62 | let mut options = Options { 63 | draw_full_vram: false, 64 | scaling: Scaling::Aspect, 65 | crop_overscan: true, 66 | 67 | pause: false, 68 | step: false, 69 | 70 | frame_limit: true, 71 | 72 | state_index: 0, 73 | }; 74 | 75 | let mut sdl_ctx_temp = sdl2::init().unwrap(); 76 | let mut audio = AudioInterface::new(&mut sdl_ctx_temp, 44100, 2, 512); 77 | let mut frontend = Frontend::create(&mut sdl_ctx_temp, 640, 480); 78 | 79 | // Disabled due to Dear ImGui version bump 80 | //let mut gui = Gui::new(&video.display); 81 | 82 | let mut system = System::new(bios_filepath.to_string(), game_filepath.to_string()); 83 | system.reset(); 84 | 85 | audio.play(); 86 | 87 | while system.running { 88 | if options.step { 89 | system.run_frame(); 90 | 91 | options.step = false; 92 | options.pause = true; 93 | } 94 | 95 | if !options.pause { 96 | system.run_frame(); 97 | } 98 | 99 | audio.push_samples(system.get_audio_samples()); 100 | frontend.update(&mut options, &mut system); 101 | frontend.render(&options, &system); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/psx/adpcm.rs: -------------------------------------------------------------------------------- 1 | pub const ADPCM_FILTERS: [[i16; 2]; 16] = [ 2 | [0, 0], 3 | [60, 0], 4 | [115, 52], 5 | [98, 55], 6 | [122, 60], 7 | [0, 0], 8 | [0, 0], 9 | [0, 0], 10 | [0, 0], 11 | [0, 0], 12 | [0, 0], 13 | [0, 0], 14 | [0, 0], 15 | [0, 0], 16 | [0, 0], 17 | [0, 0], 18 | ]; 19 | 20 | pub const ADPCM_ZIGZAG_TABLE: [[i32; 29]; 7] = [ 21 | [0, 0, 0, 0, 0, -0x2, 0xa, -0x22, 0x41, -0x54, 0x34, 0x9, -0x10a, 0x400, -0xa78, 0x234c, 0x6794, -0x1780, 0xbcd, -0x623, 0x350, -0x16d, 0x6b, 0xa, -0x10, 0x11, -0x8, 0x3, -0x1], 22 | [0, 0, 0, -0x2, 0, 0x3, -0x13, 0x3c, -0x4b, 0xa2, -0xe3, 0x132, -0x43, -0x267, 0xc9d, 0x74bb, -0x11b4, 0x9b8, -0x5bf, 0x372, -0x1a8, 0xa6, -0x1b, 0x5, 0x6, -0x8, 0x3, -0x1, 0], 23 | [0, 0, -0x1, 0x3, -0x2, -0x5, 0x1f, -0x4a, 0xb3, -0x192, 0x2b1, -0x39e, 0x4f8, -0x5a6, 0x7939, -0x5a6, 0x4f8, -0x39e, 0x2b1, -0x192, 0xb3, -0x4a, 0x1f, -0x5, -0x2, 0x3, -0x1, 0, 0], 24 | [0, -0x1, 0x3, -0x8, 0x6, 0x5, -0x1b, 0xa6, -0x1a8, 0x372, -0x5bf, 0x9b8, -0x11b4, 0x74bb, 0xc9d, -0x267, -0x43, 0x132, -0xe3, 0xa2, -0x4b, 0x3c, -0x13, 0x3, 0, -0x2, 0, 0, 0], 25 | [-0x1, 0x3, -0x8, 0x11, -0x10, 0xa, 0x6b, -0x16d, 0x350, -0x623, 0xbcd, -0x1780, 0x6794, 0x234c, -0xa78, 0x400, -0x10a, 0x9, 0x34, -0x54, 0x41, -0x22, 0xa, -0x1, 0, 0x1, 0, 0, 0], 26 | [0x2, -0x8, 0x10, -0x23, 0x2b, 0x1a, -0xeb, 0x27b, -0x548, 0xafa, -0x16fa, 0x53e0, 0x3c07, -0x1249, 0x80e, -0x347, 0x15b, -0x44, -0x17, 0x46, -0x23, 0x11, -0x5, 0, 0, 0, 0, 0, 0], 27 | [-0x5, 0x11, -0x23, 0x46, -0x17, -0x44, 0x15b, -0x347, 0x80e, -0x1249, 0x3c07, 0x53e0, -0x16fa, 0xafa, -0x548, 0x27b, -0xeb, 0x1a, 0x2b, -0x23, 0x10, -0x8, 0x2, 0, 0, 0, 0, 0, 0], 28 | ]; -------------------------------------------------------------------------------- /src/psx/bus.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{ByteOrder, LittleEndian}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::util; 5 | 6 | use super::cdrom::Cdrom; 7 | use super::exp2::Exp2; 8 | use super::gpu::Gpu; 9 | use super::intc::Intc; 10 | use super::sio0::Sio0; 11 | use super::mdec::Mdec; 12 | use super::spu::Spu; 13 | use super::timekeeper::{Device, Timekeeper}; 14 | use super::timers::Timers; 15 | 16 | #[derive(PartialEq)] 17 | pub enum BusWidth { 18 | BYTE, 19 | HALF, 20 | WORD, 21 | } 22 | 23 | #[derive(Deserialize, Serialize)] 24 | pub struct Bus { 25 | bios: Box<[u8]>, 26 | ram: Box<[u8]>, 27 | scratchpad: Box<[u8]>, 28 | 29 | cdrom: Cdrom, 30 | gpu: Gpu, 31 | mdec: Mdec, 32 | sio0: Sio0, 33 | spu: Spu, 34 | 35 | exp2: Exp2, 36 | 37 | intc: Intc, 38 | 39 | timers: Timers, 40 | } 41 | 42 | impl Bus { 43 | pub fn new(bios_filepath: &str, game_filepath: &str) -> Bus { 44 | let mut bios = util::read_file_to_box(bios_filepath); 45 | 46 | /* Enable TTY output */ 47 | bios[0x6f0c] = 0x01; 48 | bios[0x6f0d] = 0x00; 49 | bios[0x6f0e] = 0x01; 50 | bios[0x6f0f] = 0x24; 51 | bios[0x6f14] = 0xc0; 52 | bios[0x6f15] = 0xa9; 53 | bios[0x6f16] = 0x81; 54 | bios[0x6f17] = 0xaf; 55 | 56 | /* Fast boot */ 57 | //bios[0x18000] = 0x08; 58 | //bios[0x18001] = 0x00; 59 | //bios[0x18002] = 0xe0; 60 | //bios[0x18003] = 0x03; 61 | //bios[0x18004] = 0x00; 62 | //bios[0x18005] = 0x00; 63 | //bios[0x18006] = 0x00; 64 | //bios[0x18007] = 0x00; 65 | 66 | Bus { 67 | bios: bios, 68 | ram: vec![0; 0x200000].into_boxed_slice(), 69 | scratchpad: vec![0; 0x400].into_boxed_slice(), 70 | 71 | cdrom: Cdrom::new(game_filepath), 72 | gpu: Gpu::new(), 73 | mdec: Mdec::new(), 74 | sio0: Sio0::new(), 75 | spu: Spu::new(), 76 | 77 | exp2: Exp2::new(), 78 | 79 | intc: Intc::new(), 80 | 81 | timers: Timers::new(), 82 | } 83 | } 84 | 85 | pub fn reset(&mut self) { 86 | self.cdrom.reset(); 87 | self.sio0.reset(); 88 | } 89 | 90 | pub fn ram(&mut self) -> &mut Box<[u8]> { 91 | &mut self.ram 92 | } 93 | 94 | pub fn cdrom(&mut self) -> &mut Cdrom { 95 | &mut self.cdrom 96 | } 97 | 98 | pub fn gpu(&self) -> &Gpu { 99 | &self.gpu 100 | } 101 | 102 | pub fn gpu_mut(&mut self) -> &mut Gpu { 103 | &mut self.gpu 104 | } 105 | 106 | pub fn mdec(&mut self) -> &mut Mdec { 107 | &mut self.mdec 108 | } 109 | 110 | pub fn sio0(&mut self) -> &mut Sio0 { 111 | &mut self.sio0 112 | } 113 | 114 | pub fn spu(&mut self) -> &mut Spu { 115 | &mut self.spu 116 | } 117 | 118 | pub fn intc(&mut self) -> &mut Intc { 119 | &mut self.intc 120 | } 121 | 122 | pub fn tick_device_by_id(&mut self, device: Device, cycles: usize) { 123 | let intc = &mut self.intc; 124 | 125 | match device { 126 | Device::Gpu => self.gpu.tick(intc, &mut self.timers, cycles), 127 | Device::Cdrom => self.cdrom.tick(intc, &mut self.spu, cycles), 128 | Device::Spu => for _ in 0..cycles { 129 | self.spu.tick(intc); 130 | }, 131 | Device::Timers => self.timers.tick(intc, cycles), 132 | Device::Sio0 => self.sio0.tick(intc, cycles), 133 | }; 134 | } 135 | 136 | pub fn load(&mut self, tk: &mut Timekeeper, width: BusWidth, address: u32) -> (u32, bool) { 137 | let mut error = false; 138 | 139 | let value = match address { 140 | 0x0000_0000..=0x007f_ffff => { 141 | let offset = (address & 0x1f_ffff) as usize; 142 | 143 | match width { 144 | BusWidth::BYTE => self.ram[offset] as u32, 145 | BusWidth::HALF => LittleEndian::read_u16(&self.ram[offset & !0x1..]) as u32, 146 | BusWidth::WORD => LittleEndian::read_u32(&self.ram[offset & !0x3..]), 147 | } 148 | }, 149 | 0x1f00_0000..=0x1f7f_ffff => 0xffff_ffff, //println!("[MMU] [INFO] Load from EXPENSION_1 region address: 0x{:08x}", address); 0xffff_ffff }, 150 | 0x1f80_0000..=0x1f80_03ff => { 151 | let offset = (address - 0x1f80_0000) as usize; 152 | 153 | match width { 154 | BusWidth::BYTE => self.scratchpad[offset] as u32, 155 | BusWidth::HALF => LittleEndian::read_u16(&self.scratchpad[offset & !0x1..]) as u32, 156 | BusWidth::WORD => LittleEndian::read_u32(&self.scratchpad[offset & !0x3..]), 157 | } 158 | }, 159 | 0x1f80_1014 => 0x2009_31e1, 160 | 0x1f80_1060 => 0x0000_0b88, 161 | 0x1f80_1040 => { 162 | tk.sync_device(self, Device::Sio0); 163 | self.sio0.rx_data() 164 | }, 165 | 0x1f80_1044 => { 166 | tk.sync_device(self, Device::Sio0); 167 | self.sio0.status() 168 | } 169 | //0x1f80_1048 => { 170 | // tk.sync_device(self, Device::Gpu); 171 | // tk.sync_device(self, Device::Sio0); 172 | // self.sio0.read_mode() 173 | //} 174 | 0x1f80_104a => { 175 | tk.sync_device(self, Device::Sio0); 176 | self.sio0.read_control() 177 | } 178 | 0x1f80_104e => { 179 | tk.sync_device(self, Device::Sio0); 180 | self.sio0.read_baud() 181 | } 182 | 0x1f80_1070 => self.intc.read_status(), 183 | 0x1f80_1074 => self.intc.read_mask(), 184 | 0x1f80_1100..=0x1f80_112b => { 185 | tk.sync_device(self, Device::Timers); 186 | self.timers.read(address) 187 | } 188 | 0x1f80_1800..=0x1f80_1803 => { 189 | tk.sync_device(self, Device::Cdrom); 190 | 191 | if address == 0x1f80_1802 && width == BusWidth::HALF { 192 | self.cdrom.read_data_half() as u32 193 | } else { 194 | self.cdrom.read(address) as u32 195 | } 196 | } 197 | 0x1f80_1810 => { 198 | tk.sync_device(self, Device::Gpu); 199 | self.gpu.gpuread() 200 | } 201 | 0x1f80_1814 => { 202 | tk.sync_device(self, Device::Gpu); 203 | self.gpu.gpustat() 204 | } 205 | 0x1f80_1820 => self.mdec.read_data(), 206 | 0x1f80_1824 => self.mdec.read_status(), 207 | 0x1f80_1c00..=0x1f80_1fff => { 208 | tk.sync_device(self, Device::Cdrom); 209 | tk.sync_device(self, Device::Spu); 210 | 211 | match width { 212 | BusWidth::BYTE => self.spu.read16(address & !0x1) as u32, 213 | BusWidth::HALF => self.spu.read16(address) as u32, 214 | BusWidth::WORD => self.spu.read32(address) as u32, 215 | } 216 | }, 217 | 0x1f80_2000..=0x1f80_207f => self.exp2.read8(address) as u32, 218 | 0x1fc0_0000..=0x1fc7_ffff => { 219 | let offset = (address - 0x1fc0_0000) as usize; 220 | 221 | match width { 222 | BusWidth::BYTE => self.bios[offset] as u32, 223 | BusWidth::HALF => LittleEndian::read_u16(&self.bios[offset & !0x1..]) as u32, 224 | BusWidth::WORD => LittleEndian::read_u32(&self.bios[offset & !0x3..]), 225 | } 226 | }, 227 | _ => { error = true; 0 }, 228 | }; 229 | 230 | (value, error) 231 | } 232 | 233 | pub fn store(&mut self, tk: &mut Timekeeper, width: BusWidth, address: u32, value: u32) -> bool { 234 | let mut error = false; 235 | 236 | match address { 237 | 0x0000_0000..=0x007f_ffff => { 238 | let offset = (address & 0x1f_ffff) as usize; 239 | 240 | match width { 241 | BusWidth::BYTE => self.ram[offset] = value as u8, 242 | BusWidth::HALF => LittleEndian::write_u16(&mut self.ram[offset & !0x1..], value as u16), 243 | BusWidth::WORD => LittleEndian::write_u32(&mut self.ram[offset & !0x3..], value), 244 | } 245 | } 246 | 0x1f00_0000..=0x1f7f_ffff => (), //println!("[MMU] [INFO] Store to EXPENSION_1 region address: 0x{:08x}", address); 247 | 0x1f80_0000..=0x1f80_03ff => { 248 | let offset = (address - 0x1f80_0000) as usize; 249 | 250 | match width { 251 | BusWidth::BYTE => self.scratchpad[offset] = value as u8, 252 | BusWidth::HALF => LittleEndian::write_u16(&mut self.scratchpad[offset & !0x1..], value as u16), 253 | BusWidth::WORD => LittleEndian::write_u32(&mut self.scratchpad[offset & !0x3..], value), 254 | } 255 | } 256 | 0x1f80_1000..=0x1f80_1023 => (), //println!("[BUS] [INFO] Store to MEM_CTRL region address: 0x{:08x}", address), 257 | 0x1f80_1040 => { 258 | tk.sync_device(self, Device::Gpu); 259 | tk.sync_device(self, Device::Sio0); 260 | self.sio0.tx_data(value) 261 | } 262 | 0x1f80_1048 => { 263 | tk.sync_device(self, Device::Gpu); 264 | tk.sync_device(self, Device::Sio0); 265 | self.sio0.write_mode(value as u16) 266 | } 267 | 0x1f80_104a => { 268 | tk.sync_device(self, Device::Gpu); 269 | tk.sync_device(self, Device::Sio0); 270 | self.sio0.write_control(value as u16) 271 | } 272 | 0x1f80_104e => { 273 | tk.sync_device(self, Device::Gpu); 274 | tk.sync_device(self, Device::Sio0); 275 | self.sio0.write_baud(value as u16) 276 | } 277 | 0x1f80_1060 => (), //println!("[BUS] [INFO] Store to MEM_CTRL region address: 0x{:08x}", address), 278 | 0x1f80_1070 => self.intc.acknowledge_irq(value), 279 | 0x1f80_1074 => self.intc.write_mask(value), 280 | 0x1f80_1100..=0x1f80_112b => { 281 | tk.sync_device(self, Device::Timers); 282 | self.timers.write(address, value) 283 | } 284 | 0x1f80_1800..=0x1f80_1803 => { 285 | tk.sync_device(self, Device::Cdrom); 286 | self.cdrom.write(address, value as u8) 287 | } 288 | 0x1f80_1810 => { 289 | tk.sync_device(self, Device::Gpu); 290 | self.gpu.gp0_write(value) 291 | } 292 | 0x1f80_1814 => { 293 | tk.sync_device(self, Device::Gpu); 294 | self.gpu.execute_gp1_command(value) 295 | } 296 | 0x1f80_1820 => self.mdec.write_command(value), 297 | 0x1f80_1824 => self.mdec.write_control(value), 298 | 0x1f80_1c00..=0x1f80_1fff => { 299 | tk.sync_device(self, Device::Cdrom); 300 | tk.sync_device(self, Device::Spu); 301 | 302 | match width { 303 | BusWidth::HALF => self.spu.write16(address, value as u16), 304 | _ => panic!("[BUS] [ERROR] Unsupported SPU width"), 305 | } 306 | }, 307 | 0x1f80_2000..=0x1f80_207f => self.exp2.write8(address, value as u8), 308 | _ => { 309 | error = true; 310 | //println!("[BUS] [ERROR] Store to unrecognised address 0x{:08x}", address) 311 | }, 312 | }; 313 | 314 | error 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/psx/cdrom/container/bin.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Seek}; 2 | use std::{fs, path}; 3 | 4 | use super::Container; 5 | 6 | pub struct Bin { 7 | file: fs::File, 8 | } 9 | 10 | impl Container for Bin { 11 | fn open(filepath: &path::Path) -> Result, String> { 12 | if !filepath.exists() { 13 | return Err("File does not exist.".to_string()); 14 | } 15 | 16 | let file; 17 | 18 | match fs::File::open(filepath) { 19 | Ok(f) => file = f, 20 | Err(e) => return Err(e.to_string()), 21 | }; 22 | 23 | Ok(Box::new(Self { file: file })) 24 | } 25 | 26 | fn read(&mut self, lba: usize, buffer: &mut [u8; 2352]) -> Result<(), String> { 27 | let offset = (lba * 2352) as u64; 28 | 29 | if let Err(e) = self.file.seek(io::SeekFrom::Start(offset)) { 30 | return Err(e.to_string()); 31 | } 32 | 33 | if let Err(e) = self.file.read_exact(buffer) { 34 | return Err(e.to_string()); 35 | } 36 | 37 | Ok(()) 38 | } 39 | } -------------------------------------------------------------------------------- /src/psx/cdrom/container/mod.rs: -------------------------------------------------------------------------------- 1 | mod bin; 2 | mod no_disk; 3 | 4 | use std::path; 5 | 6 | pub use bin::Bin; 7 | pub use no_disk::NoDisk; 8 | 9 | pub trait Container { 10 | fn open(filepath: &path::Path) -> Result, String>; 11 | fn read(&mut self, lba: usize, buffer: &mut [u8; 2352]) -> Result<(), String>; 12 | } -------------------------------------------------------------------------------- /src/psx/cdrom/container/no_disk.rs: -------------------------------------------------------------------------------- 1 | use std::path; 2 | 3 | use super::Container; 4 | 5 | pub struct NoDisk; 6 | 7 | impl Container for NoDisk { 8 | fn open(_: &path::Path) -> Result, String> { 9 | Ok(Box::new(Self)) 10 | } 11 | 12 | fn read(&mut self, _: usize, buffer: &mut [u8; 2352]) -> Result<(), String> { 13 | for i in 0..buffer.len() { buffer[i] = 0; } 14 | 15 | Err("No disk inserted".to_string()) 16 | } 17 | } -------------------------------------------------------------------------------- /src/psx/cdrom/headers.rs: -------------------------------------------------------------------------------- 1 | use super::Timecode; 2 | 3 | #[allow(dead_code)] 4 | pub struct Header { 5 | timecode: Timecode, 6 | mode: u8, 7 | } 8 | 9 | #[allow(dead_code)] 10 | pub struct Subheader { 11 | file: u8, 12 | channel: u8, 13 | submode: u8, 14 | coding_info: u8, 15 | } -------------------------------------------------------------------------------- /src/psx/cdrom/helpers.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub fn bcd_to_u8(value: u8) -> u8 { 3 | ((value >> 4) * 10) + (value & 0xf) 4 | } 5 | 6 | #[allow(dead_code)] 7 | pub fn u8_to_bcd(value: u8) -> u8 { 8 | ((value / 10) << 4) | (value % 10) 9 | } -------------------------------------------------------------------------------- /src/psx/cdrom/timecode.rs: -------------------------------------------------------------------------------- 1 | use super::helpers; 2 | 3 | #[derive(PartialEq, Eq)] 4 | pub struct Timecode { 5 | minute: usize, 6 | second: usize, 7 | sector: usize, 8 | } 9 | 10 | #[allow(dead_code)] 11 | impl Timecode { 12 | pub fn from_bcd(minute: u8, second: u8, sector: u8) -> Self { 13 | Self { 14 | minute: helpers::bcd_to_u8(minute) as usize, 15 | second: helpers::bcd_to_u8(second) as usize, 16 | sector: helpers::bcd_to_u8(sector) as usize, 17 | } 18 | } 19 | 20 | pub fn to_bcd(&self) -> (u8, u8, u8) { 21 | let minute = helpers::bcd_to_u8(self.minute as u8); 22 | let second = helpers::bcd_to_u8(self.second as u8); 23 | let sector = helpers::bcd_to_u8(self.sector as u8); 24 | 25 | (minute, second, sector) 26 | } 27 | 28 | pub fn to_lba(&self) -> usize { 29 | self.minute * 60 * 75 + 30 | self.second * 75 + 31 | self.sector 32 | } 33 | 34 | pub fn advance(&mut self) { 35 | self.sector += 1; 36 | 37 | if self.sector == 75 { 38 | self.sector = 0; 39 | self.second += 1; 40 | 41 | if self.second == 60 { 42 | self.second = 0; 43 | self.minute += 1; 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/psx/cpu/cop0.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, PartialEq)] 4 | pub enum Exception { 5 | Interrupt = 0, 6 | AddrLoad = 4, 7 | AddrStore = 5, 8 | IBusError = 6, 9 | DBusError = 7, 10 | Syscall = 8, 11 | Breakpoint = 9, 12 | Reserved = 10, 13 | Overflow = 12, 14 | } 15 | 16 | #[derive(Deserialize, Serialize)] 17 | struct Dcic { 18 | trap: bool, 19 | user_debug: bool, 20 | kernel_debug: bool, 21 | trace: bool, 22 | data_write: bool, 23 | data_read: bool, 24 | data_breakpoint: bool, 25 | code_breakpoint: bool, 26 | master_debug: bool, 27 | hit_trace: bool, 28 | hit_write: bool, 29 | hit_read: bool, 30 | hit_data: bool, 31 | hit_code: bool, 32 | hit_debug: bool, 33 | } 34 | 35 | impl Dcic { 36 | pub fn new() -> Dcic { 37 | Dcic { 38 | trap: false, 39 | user_debug: false, 40 | kernel_debug: false, 41 | trace: false, 42 | data_write: false, 43 | data_read: false, 44 | data_breakpoint: false, 45 | code_breakpoint: false, 46 | master_debug: false, 47 | hit_trace: false, 48 | hit_write: false, 49 | hit_read: false, 50 | hit_data: false, 51 | hit_code: false, 52 | hit_debug: false, 53 | } 54 | } 55 | 56 | pub fn reset(&mut self) { 57 | self.trap = false; 58 | self.user_debug = false; 59 | self.kernel_debug = false; 60 | self.trace = false; 61 | self.data_write = false; 62 | self.data_read = false; 63 | self.data_breakpoint = false; 64 | self.code_breakpoint = false; 65 | self.master_debug = false; 66 | self.hit_trace = false; 67 | self.hit_write = false; 68 | self.hit_read = false; 69 | self.hit_data = false; 70 | self.hit_code = false; 71 | self.hit_debug = false; 72 | } 73 | 74 | pub fn read(&self) -> u32 { 75 | (self.trap as u32) << 31 76 | | (self.user_debug as u32) << 30 77 | | (self.kernel_debug as u32) << 29 78 | | (self.trace as u32) << 28 79 | | (self.data_write as u32) << 27 80 | | (self.data_read as u32) << 26 81 | | (self.data_breakpoint as u32) << 25 82 | | (self.code_breakpoint as u32) << 24 83 | | (self.master_debug as u32) << 23 84 | | (self.hit_trace as u32) << 5 85 | | (self.hit_write as u32) << 4 86 | | (self.hit_read as u32) << 3 87 | | (self.hit_data as u32) << 2 88 | | (self.hit_code as u32) << 1 89 | | (self.hit_debug as u32) 90 | } 91 | 92 | pub fn write(&mut self, value: u32) { 93 | self.trap = (value & 0x8000_0000) != 0; 94 | self.user_debug = (value & 0x4000_0000) != 0; 95 | self.kernel_debug = (value & 0x2000_0000) != 0; 96 | self.trace = (value & 0x1000_0000) != 0; 97 | self.data_write = (value & 0x800_0000) != 0; 98 | self.data_read = (value & 0x400_0000) != 0; 99 | self.data_breakpoint = (value & 0x200_0000) != 0; 100 | self.code_breakpoint = (value & 0x100_0000) != 0; 101 | self.master_debug = (value & 0x80_0000) != 0; 102 | self.hit_trace = (value & 0x20) != 0; 103 | self.hit_write = (value & 0x10) != 0; 104 | self.hit_read = (value & 0x8) != 0; 105 | self.hit_data = (value & 0x4) != 0; 106 | self.hit_code = (value & 0x2) != 0; 107 | self.hit_debug = (value & 0x1) != 0; 108 | } 109 | } 110 | 111 | #[derive(Deserialize, Serialize)] 112 | struct Status { 113 | coprocessor_usability: [bool; 4], 114 | reverse_endianness: bool, 115 | bootstrap_exception_vector: bool, 116 | tlb_shutdown: bool, 117 | parity_error: bool, 118 | cache_miss: bool, 119 | parity_zero: bool, 120 | swap_caches: bool, 121 | isolate_cache: bool, 122 | interrupt_mask: u8, 123 | kernel_user_old: bool, 124 | interrupt_enable_old: bool, 125 | kernel_user_previous: bool, 126 | interrupt_enable_previous: bool, 127 | kernel_user_current: bool, 128 | interrupt_enable_current: bool, 129 | } 130 | 131 | impl Status { 132 | pub fn new() -> Status { 133 | Status { 134 | coprocessor_usability: [false; 4], 135 | reverse_endianness: false, 136 | bootstrap_exception_vector: false, 137 | tlb_shutdown: false, 138 | parity_error: false, 139 | cache_miss: false, 140 | parity_zero: false, 141 | swap_caches: false, 142 | isolate_cache: false, 143 | interrupt_mask: 0, 144 | kernel_user_old: false, 145 | interrupt_enable_old: false, 146 | kernel_user_previous: false, 147 | interrupt_enable_previous: false, 148 | kernel_user_current: false, 149 | interrupt_enable_current: false, 150 | } 151 | } 152 | 153 | pub fn reset(&mut self) { 154 | self.coprocessor_usability = [false; 4]; 155 | self.reverse_endianness = false; 156 | self.bootstrap_exception_vector = true; 157 | self.tlb_shutdown = true; 158 | self.parity_error = false; 159 | self.cache_miss = false; 160 | self.parity_zero = false; 161 | self.swap_caches = false; 162 | self.isolate_cache = false; 163 | self.interrupt_mask = 0; 164 | self.kernel_user_old = false; 165 | self.interrupt_enable_old = false; 166 | self.kernel_user_previous = false; 167 | self.interrupt_enable_previous = false; 168 | self.kernel_user_current = false; 169 | self.interrupt_enable_current = false; 170 | } 171 | 172 | pub fn read(&self) -> u32 { 173 | (self.coprocessor_usability[3] as u32) << 31 174 | | (self.coprocessor_usability[2] as u32) << 30 175 | | (self.coprocessor_usability[1] as u32) << 29 176 | | (self.coprocessor_usability[0] as u32) << 28 177 | | (self.reverse_endianness as u32) << 25 178 | | (self.bootstrap_exception_vector as u32) << 22 179 | | (self.tlb_shutdown as u32) << 21 180 | | (self.parity_error as u32) << 20 181 | | (self.cache_miss as u32) << 19 182 | | (self.parity_zero as u32) << 18 183 | | (self.swap_caches as u32) << 17 184 | | (self.isolate_cache as u32) << 16 185 | | (self.interrupt_mask as u32) << 8 186 | | (self.kernel_user_old as u32) << 5 187 | | (self.interrupt_enable_old as u32) << 4 188 | | (self.kernel_user_previous as u32) << 3 189 | | (self.interrupt_enable_previous as u32) << 2 190 | | (self.kernel_user_current as u32) << 1 191 | | (self.interrupt_enable_current as u32) 192 | } 193 | 194 | pub fn write(&mut self, value: u32) { 195 | self.coprocessor_usability[3] = (value & 0x8000_0000) != 0; 196 | self.coprocessor_usability[2] = (value & 0x4000_0000) != 0; 197 | self.coprocessor_usability[1] = (value & 0x2000_0000) != 0; 198 | self.coprocessor_usability[0] = (value & 0x1000_0000) != 0; 199 | self.reverse_endianness = (value & 0x0200_0000) != 0; 200 | self.bootstrap_exception_vector = (value & 0x0040_0000) != 0; 201 | self.tlb_shutdown = (value & 0x0020_0000) != 0; 202 | self.parity_error = (value & 0x0010_0000) != 0; 203 | self.cache_miss = (value & 0x0008_0000) != 0; 204 | self.parity_zero = (value & 0x0004_0000) != 0; 205 | self.swap_caches = (value & 0x0002_0000) != 0; 206 | self.isolate_cache = (value & 0x0001_0000) != 0; 207 | self.interrupt_mask = (value >> 8) as u8; 208 | self.kernel_user_old = (value & 0x0000_0020) != 0; 209 | self.interrupt_enable_old = (value & 0x0000_0010) != 0; 210 | self.kernel_user_previous = (value & 0x0000_0008) != 0; 211 | self.interrupt_enable_previous = (value & 0x0000_0004) != 0; 212 | self.kernel_user_current = (value & 0x0000_0002) != 0; 213 | self.interrupt_enable_current = (value & 0x0000_0001) != 0; 214 | } 215 | 216 | pub fn enter_exception(&mut self) { 217 | self.kernel_user_old = self.kernel_user_previous; 218 | self.interrupt_enable_old = self.interrupt_enable_previous; 219 | 220 | self.kernel_user_previous = self.kernel_user_current; 221 | self.interrupt_enable_previous = self.interrupt_enable_current; 222 | 223 | self.kernel_user_current = false; 224 | self.interrupt_enable_current = false; 225 | } 226 | 227 | pub fn leave_exception(&mut self) { 228 | self.kernel_user_current = self.kernel_user_previous; 229 | self.interrupt_enable_current = self.interrupt_enable_previous; 230 | 231 | self.kernel_user_previous = self.kernel_user_old; 232 | self.interrupt_enable_previous = self.interrupt_enable_old; 233 | } 234 | } 235 | 236 | #[derive(Deserialize, Serialize)] 237 | struct Cause { 238 | branch_delay: bool, 239 | branch_taken: bool, 240 | coprocessor_exception: u8, 241 | interrupt_pending: u8, 242 | exception_code: u8, 243 | } 244 | 245 | impl Cause { 246 | pub fn new() -> Cause { 247 | Cause { 248 | branch_delay: false, 249 | branch_taken: false, 250 | coprocessor_exception: 0, 251 | interrupt_pending: 0, 252 | exception_code: 0, 253 | } 254 | } 255 | 256 | pub fn reset(&mut self) { 257 | self.branch_delay = false; 258 | self.branch_taken = false; 259 | self.coprocessor_exception = 0; 260 | self.interrupt_pending = 0; 261 | self.exception_code = 0; 262 | } 263 | 264 | pub fn read(&self) -> u32 { 265 | (self.branch_delay as u32) << 31 266 | | (self.branch_taken as u32) << 30 267 | | ((self.coprocessor_exception & 0x03) as u32) << 28 268 | | (self.interrupt_pending as u32) << 8 269 | | ((self.exception_code & 0x1f) as u32) << 2 270 | } 271 | 272 | pub fn write(&mut self, value: u32) { 273 | self.interrupt_pending &= !0x03; 274 | self.interrupt_pending |= ((value >> 8) & 0x03) as u8; 275 | } 276 | 277 | pub fn set_interrupt_bit(&mut self) { 278 | self.interrupt_pending |= 0x4; 279 | } 280 | 281 | pub fn clear_interrupt_bit(&mut self) { 282 | self.interrupt_pending &= !0x4; 283 | } 284 | 285 | pub fn enter_exception(&mut self, exception: Exception, bd: bool, bt: bool, coprocessor: u8) { 286 | self.exception_code = exception as u8; 287 | self.coprocessor_exception = coprocessor; 288 | self.branch_delay = bd; 289 | self.branch_taken = bt; 290 | } 291 | } 292 | 293 | #[derive(Deserialize, Serialize)] 294 | pub struct Cop0 { 295 | bpc: u32, 296 | bda: u32, 297 | jumpdest: u32, 298 | dcic: Dcic, 299 | bad_vaddr: u32, 300 | bdam: u32, 301 | bpcm: u32, 302 | status: Status, 303 | cause: Cause, 304 | epc: u32, 305 | } 306 | 307 | impl Cop0 { 308 | pub fn new() -> Cop0 { 309 | Cop0 { 310 | bpc: 0, 311 | bda: 0, 312 | jumpdest: 0, 313 | dcic: Dcic::new(), 314 | bad_vaddr: 0, 315 | bdam: 0, 316 | bpcm: 0, 317 | status: Status::new(), 318 | cause: Cause::new(), 319 | epc: 0, 320 | } 321 | } 322 | 323 | pub fn reset(&mut self) { 324 | self.dcic.reset(); 325 | self.bpcm = 0xffff_ffff; 326 | self.bdam = 0xffff_ffff; 327 | self.status.reset(); 328 | self.cause.reset(); 329 | } 330 | 331 | pub fn read(&self, index: usize) -> u32 { 332 | match index { 333 | 3 => self.bpc, 334 | 5 => self.bda, 335 | 6 => self.jumpdest, 336 | 7 => self.dcic.read(), 337 | 8 => self.bad_vaddr, 338 | 9 => self.bdam, 339 | 11 => self.bpcm, 340 | 12 => self.status.read(), 341 | 13 => self.cause.read(), 342 | 14 => self.epc, 343 | 15 => 0x0000_0002, 344 | _ => panic!( 345 | "[COP0] [ERROR] Read from unimplemented Cop0 register {}", 346 | index 347 | ), 348 | } 349 | } 350 | 351 | pub fn write(&mut self, index: usize, value: u32) { 352 | match index { 353 | 3 => { 354 | self.bpc = value; 355 | //println!("Setting BPC to 0x{:08x}", self.bpc); 356 | }, 357 | 5 => { 358 | self.bda = value; 359 | //println!("Setting BDA to 0x{:08x}", self.bda); 360 | }, 361 | 6 => (), 362 | 7 => self.dcic.write(value), 363 | 9 => { 364 | self.bdam = value; 365 | //println!("Setting BDAM to 0x{:08x}", self.bdam); 366 | }, 367 | 11 => { 368 | self.bpcm = value; 369 | //println!("Setting BPCM to 0x{:08x}", self.bpcm); 370 | }, 371 | 12 => self.status.write(value), 372 | 13 => self.cause.write(value), 373 | _ => panic!( 374 | "[COP0] [ERROR] Write to unimplemented Cop0 register {}", 375 | index 376 | ), 377 | } 378 | } 379 | 380 | pub fn enter_exception(&mut self, epc: u32, exception: Exception, bd: bool, bt: bool, coprocessor: u8) { 381 | self.epc = epc; 382 | self.status.enter_exception(); 383 | self.cause.enter_exception(exception, bd, bt, coprocessor); 384 | } 385 | 386 | pub fn leave_exception(&mut self) { 387 | self.status.leave_exception(); 388 | } 389 | 390 | pub fn exception_vectors(&self) -> bool { 391 | self.status.bootstrap_exception_vector 392 | } 393 | 394 | pub fn set_bad_vaddr(&mut self, value: u32) { 395 | self.bad_vaddr = value; 396 | } 397 | 398 | pub fn iec(&self) -> bool { 399 | self.status.interrupt_enable_current 400 | } 401 | 402 | pub fn im(&self) -> bool { 403 | (self.status.interrupt_mask & self.cause.interrupt_pending) != 0 404 | } 405 | 406 | pub fn set_jumpdest(&mut self, value: u32) { 407 | self.jumpdest = value; 408 | } 409 | 410 | pub fn test_code(&mut self, test: u32) -> bool { 411 | if !self.dcic.master_debug { 412 | return false; 413 | } 414 | 415 | let kernel = test >= 0x8000_0000; 416 | let user = !kernel; 417 | 418 | if !(self.dcic.kernel_debug && kernel) 419 | && !(self.dcic.user_debug && user) { 420 | return false; 421 | } 422 | 423 | if !self.dcic.code_breakpoint { 424 | return false; 425 | } 426 | 427 | if ((test ^ self.bpc) & self.bpcm) != 0 { 428 | return false; 429 | } 430 | 431 | self.dcic.hit_debug = true; 432 | self.dcic.hit_code = true; 433 | 434 | self.dcic.trap 435 | } 436 | 437 | pub fn test_read(&mut self, test: u32) -> bool { 438 | if !self.dcic.master_debug { 439 | return false; 440 | } 441 | 442 | let kernel = test >= 0x8000_0000; 443 | let user = !kernel; 444 | 445 | if !(self.dcic.kernel_debug && kernel) 446 | && !(self.dcic.user_debug && user) { 447 | return false; 448 | } 449 | 450 | if !self.dcic.data_breakpoint { 451 | return false; 452 | } 453 | 454 | if !self.dcic.data_read { 455 | return false; 456 | } 457 | 458 | if ((test ^ self.bda) & self.bdam) != 0 { 459 | return false; 460 | } 461 | 462 | self.dcic.hit_debug = true; 463 | self.dcic.hit_data = true; 464 | self.dcic.hit_read = true; 465 | 466 | self.dcic.trap 467 | } 468 | 469 | pub fn test_write(&mut self, test: u32) -> bool { 470 | if !self.dcic.master_debug { 471 | return false; 472 | } 473 | 474 | //if test == 0x801f0001 { 475 | // println!("BDA:\t0x{:08x}", self.bda); 476 | // println!("BDAM:\t0x{:08x}", self.bdam); 477 | // println!("TEST:\t0x{:08x}", test); 478 | //} 479 | 480 | let kernel = test >= 0x8000_0000; 481 | let user = !kernel; 482 | 483 | if !(self.dcic.kernel_debug && kernel) 484 | && !(self.dcic.user_debug && user) { 485 | return false; 486 | } 487 | 488 | //if test == 0x801f0001 { 489 | // println!("a"); 490 | //} 491 | 492 | if !self.dcic.data_breakpoint { 493 | return false; 494 | } 495 | 496 | if !self.dcic.data_write { 497 | return false; 498 | } 499 | 500 | if ((test ^ self.bda) & self.bdam) != 0 { 501 | return false; 502 | } 503 | 504 | self.dcic.hit_debug = true; 505 | self.dcic.hit_data = true; 506 | self.dcic.hit_write = true; 507 | 508 | self.dcic.trap 509 | } 510 | 511 | pub fn set_interrupt_bit(&mut self) { 512 | self.cause.set_interrupt_bit(); 513 | } 514 | 515 | pub fn clear_interrupt_bit(&mut self) { 516 | self.cause.clear_interrupt_bit(); 517 | } 518 | 519 | pub fn isolate_cache(&self) -> bool { 520 | self.status.isolate_cache 521 | } 522 | } -------------------------------------------------------------------------------- /src/psx/cpu/dmac.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{ByteOrder, LittleEndian}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::Bus; 5 | use super::super::intc::{Intc, Interrupt}; 6 | 7 | #[derive(Clone, Copy, Deserialize, Serialize)] 8 | pub enum SyncMode { 9 | Manual, 10 | Request, 11 | LinkedList, 12 | } 13 | 14 | #[derive(Clone, Copy, Deserialize, Serialize)] 15 | pub enum Step { 16 | Forward, 17 | Backward, 18 | } 19 | 20 | #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] 21 | pub enum Direction { 22 | ToRam, 23 | FromRam, 24 | } 25 | 26 | #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] 27 | pub enum DmacPort { 28 | MDECIn, 29 | MDECOut, 30 | GPU, 31 | CDROM, 32 | SPU, 33 | PIO, 34 | OTC, 35 | Control, 36 | } 37 | 38 | impl DmacPort { 39 | pub fn to(port: usize) -> DmacPort { 40 | use self::DmacPort::*; 41 | 42 | match port { 43 | 0 => MDECIn, 44 | 1 => MDECOut, 45 | 2 => GPU, 46 | 3 => CDROM, 47 | 4 => SPU, 48 | 5 => PIO, 49 | 6 => OTC, 50 | 7 => Control, 51 | _ => panic!("[ERROR] [DMAC] Invalid port {}", port), 52 | } 53 | } 54 | 55 | pub fn from(port: DmacPort) -> usize { 56 | use self::DmacPort::*; 57 | 58 | match port { 59 | MDECIn => 0, 60 | MDECOut => 1, 61 | GPU => 2, 62 | CDROM => 3, 63 | SPU => 4, 64 | PIO => 5, 65 | OTC => 6, 66 | Control => 7, 67 | } 68 | } 69 | } 70 | 71 | #[derive(Clone, Copy, Deserialize, Serialize)] 72 | pub struct DmacChannel { 73 | base_address: u32, 74 | 75 | block_size: u16, 76 | block_amount: u16, 77 | 78 | chopping_enabled: bool, 79 | 80 | trigger: bool, 81 | enable: bool, 82 | sync: SyncMode, 83 | step: Step, 84 | direction: Direction, 85 | } 86 | 87 | impl DmacChannel { 88 | pub fn new() -> DmacChannel { 89 | DmacChannel { 90 | base_address: 0, 91 | 92 | block_size: 0, 93 | block_amount: 0, 94 | 95 | chopping_enabled: false, 96 | 97 | trigger: false, 98 | enable: false, 99 | sync: SyncMode::Manual, 100 | step: Step::Forward, 101 | direction: Direction::ToRam, 102 | } 103 | } 104 | 105 | pub fn base_address(&self) -> u32 { 106 | self.base_address & 0xfffffc 107 | } 108 | 109 | pub fn block_size(&self) -> usize { 110 | if self.block_size == 0 { 111 | return 0x10000; 112 | } 113 | 114 | self.block_size as usize 115 | } 116 | 117 | pub fn sync(&self) -> SyncMode { 118 | self.sync 119 | } 120 | 121 | pub fn step(&self) -> Step { 122 | self.step 123 | } 124 | 125 | pub fn direction(&self) -> Direction { 126 | self.direction 127 | } 128 | 129 | pub fn active(&self) -> bool { 130 | let trigger = match self.sync { 131 | SyncMode::Manual => self.trigger, 132 | _ => true, 133 | }; 134 | 135 | self.enable & trigger 136 | } 137 | 138 | pub fn finish(&mut self) { 139 | self.trigger = false; 140 | self.enable = false; 141 | } 142 | 143 | pub fn block_control_read(&self) -> u32 { 144 | ((self.block_amount as u32) << 16) | (self.block_size as u32) 145 | } 146 | 147 | pub fn block_control_write(&mut self, value: u32) { 148 | self.block_size = value as u16; 149 | self.block_amount = (value >> 16) as u16; 150 | } 151 | 152 | pub fn channel_control_read(&self) -> u32 { 153 | let mut value = 0; 154 | 155 | value |= (self.trigger as u32) << 28; 156 | value |= (self.enable as u32) << 24; 157 | value |= match self.sync { 158 | SyncMode::Manual => 0x000, 159 | SyncMode::Request => 0x200, 160 | SyncMode::LinkedList => 0x400, 161 | }; 162 | value |= match self.step { 163 | Step::Forward => 0x0, 164 | Step::Backward => 0x2, 165 | }; 166 | value |= match self.direction { 167 | Direction::ToRam => 0x0, 168 | Direction::FromRam => 0x1, 169 | }; 170 | 171 | value 172 | } 173 | 174 | pub fn channel_control_write(&mut self, value: u32) { 175 | let old_enable = self.enable; 176 | 177 | self.trigger = (value & 0x1000_0000) != 0; 178 | self.enable = (value & 0x0100_0000) != 0; 179 | self.sync = match (value & 0x600) >> 9 { 180 | 0 => SyncMode::Manual, 181 | 1 => SyncMode::Request, 182 | 2 => SyncMode::LinkedList, 183 | 3 => panic!("[DMAC] [ERROR] Invalid SyncMode"), 184 | _ => unreachable!(), 185 | }; 186 | 187 | self.chopping_enabled = (value & 0x100) != 0; 188 | 189 | self.step = match (value & 0x2) != 0 { 190 | true => Step::Backward, 191 | false => Step::Forward, 192 | }; 193 | self.direction = match (value & 0x1) != 0 { 194 | true => Direction::FromRam, 195 | false => Direction::ToRam, 196 | }; 197 | 198 | if old_enable && !self.enable { 199 | panic!("disabled active transfer"); 200 | } 201 | } 202 | } 203 | 204 | #[derive(Deserialize, Serialize)] 205 | pub struct Dmac { 206 | channels: [DmacChannel; 7], 207 | control: u32, 208 | interrupt: u32, 209 | 210 | gap_ticks: isize, 211 | gap_started: bool, 212 | 213 | active_port: Option, 214 | active_address: u32, 215 | active_remaining: usize, 216 | active_count: usize, 217 | } 218 | 219 | impl Dmac { 220 | pub fn new() -> Dmac { 221 | Dmac { 222 | channels: [DmacChannel::new(); 7], 223 | control: 0x07654321, 224 | interrupt: 0, 225 | 226 | gap_ticks: 0, 227 | gap_started: false, 228 | 229 | active_port: None, 230 | active_address: 0, 231 | active_remaining: 0, 232 | active_count: 0, 233 | } 234 | } 235 | 236 | fn tick_manual(&mut self, port: DmacPort, bus: &mut Bus) { 237 | let channel = self.channel(port); 238 | 239 | let step = channel.step(); 240 | let direction = channel.direction(); 241 | 242 | match direction { 243 | Direction::ToRam => { 244 | match port { 245 | DmacPort::CDROM => { 246 | let data = bus.cdrom().data_dma(); 247 | 248 | LittleEndian::write_u32( 249 | &mut bus.ram()[self.active_address as usize..], 250 | data, 251 | ); 252 | 253 | self.active_address = match step { 254 | Step::Forward => self.active_address.wrapping_add(4), 255 | Step::Backward => self.active_address.wrapping_sub(4), 256 | } & 0x1f_fffc; 257 | 258 | self.active_remaining -= 1; 259 | } 260 | DmacPort::OTC => { 261 | let value = match self.active_remaining { 262 | 1 => 0xff_ffff, 263 | _ => self.active_address.wrapping_sub(4) & 0x1f_fffc, 264 | }; 265 | 266 | LittleEndian::write_u32( 267 | &mut bus.ram()[self.active_address as usize..], 268 | value, 269 | ); 270 | 271 | self.active_address = value & 0x1f_fffc; 272 | self.active_remaining -= 1; 273 | } 274 | _ => panic!("[DMAC] [ERROR] Unsupported DMA Port {:?} for Manual", port), 275 | }; 276 | } 277 | Direction::FromRam => { 278 | match port { 279 | DmacPort::GPU => { 280 | let data = 281 | LittleEndian::read_u32(&bus.ram()[self.active_address as usize..]); 282 | 283 | bus.gpu_mut().gp0_write(data); 284 | 285 | self.active_address = match step { 286 | Step::Forward => self.active_address.wrapping_add(4), 287 | Step::Backward => self.active_address.wrapping_sub(4), 288 | } & 0x1f_fffc; 289 | 290 | self.active_remaining -= 1; 291 | } 292 | _ => panic!("[DMAC] [ERROR] Unsupported DMA Port {:?} for Manual", port), 293 | }; 294 | } 295 | }; 296 | 297 | if self.active_remaining == 0 { 298 | self.active_count += channel.block_size(); 299 | self.active_port = None; 300 | self.channel_mut(port).finish(); 301 | self.finish_set_interrupt(bus.intc(), port); 302 | } 303 | } 304 | 305 | fn tick_request(&mut self, port: DmacPort, bus: &mut Bus) { 306 | let channel = self.channel(port); 307 | 308 | let step = channel.step(); 309 | let direction = channel.direction(); 310 | 311 | match direction { 312 | Direction::ToRam => { 313 | match port { 314 | DmacPort::MDECOut => { 315 | let data = bus.mdec().read_data(); 316 | 317 | LittleEndian::write_u32( 318 | &mut bus.ram()[self.active_address as usize..], 319 | data, 320 | ); 321 | 322 | self.active_address = match step { 323 | Step::Forward => self.active_address.wrapping_add(4), 324 | Step::Backward => self.active_address.wrapping_sub(4), 325 | } & 0x1f_fffc; 326 | 327 | self.active_remaining -= 1; 328 | } 329 | DmacPort::GPU => { 330 | let data = bus.gpu_mut().gpuread(); 331 | 332 | LittleEndian::write_u32( 333 | &mut bus.ram()[self.active_address as usize..], 334 | data, 335 | ); 336 | 337 | self.active_address = match step { 338 | Step::Forward => self.active_address.wrapping_add(4), 339 | Step::Backward => self.active_address.wrapping_sub(4), 340 | } & 0x1f_fffc; 341 | 342 | self.active_remaining -= 1; 343 | } 344 | DmacPort::SPU => { 345 | let data = bus.spu().dma_read(); 346 | 347 | LittleEndian::write_u32( 348 | &mut bus.ram()[self.active_address as usize..], 349 | data, 350 | ); 351 | 352 | self.active_address = match step { 353 | Step::Forward => self.active_address.wrapping_add(4), 354 | Step::Backward => self.active_address.wrapping_sub(4), 355 | } & 0x1f_fffc; 356 | 357 | self.active_remaining -= 1; 358 | } 359 | _ => panic!("[DMAC] [ERROR] Unsupported DMA Port {:?} for Request", port), 360 | }; 361 | } 362 | Direction::FromRam => { 363 | match port { 364 | DmacPort::MDECIn => { 365 | let data = 366 | LittleEndian::read_u32(&bus.ram()[self.active_address as usize..]); 367 | 368 | bus.mdec().write_command(data); 369 | 370 | self.active_address = match step { 371 | Step::Forward => self.active_address.wrapping_add(4), 372 | Step::Backward => self.active_address.wrapping_sub(4), 373 | } & 0x1f_fffc; 374 | 375 | self.active_remaining -= 1; 376 | } 377 | DmacPort::GPU => { 378 | let data = 379 | LittleEndian::read_u32(&bus.ram()[self.active_address as usize..]); 380 | 381 | bus.gpu_mut().gp0_write(data); 382 | 383 | self.active_address = match step { 384 | Step::Forward => self.active_address.wrapping_add(4), 385 | Step::Backward => self.active_address.wrapping_sub(4), 386 | } & 0x1f_fffc; 387 | 388 | self.active_remaining -= 1; 389 | } 390 | DmacPort::SPU => { 391 | let data = 392 | LittleEndian::read_u32(&bus.ram()[self.active_address as usize..]); 393 | 394 | bus.spu().dma_write(data); 395 | 396 | self.active_address = match step { 397 | Step::Forward => self.active_address.wrapping_add(4), 398 | Step::Backward => self.active_address.wrapping_sub(4), 399 | } & 0x1f_fffc; 400 | 401 | self.active_remaining -= 1; 402 | } 403 | _ => panic!("[DMAC] [ERROR] Unsupported DMA Port {:?} for Request", port), 404 | }; 405 | } 406 | }; 407 | 408 | if self.active_remaining == 0 { 409 | self.active_count += channel.block_size(); 410 | 411 | let channel = self.channel_mut(port); 412 | channel.block_amount -= 1; 413 | channel.base_address += channel.block_size() as u32; 414 | 415 | let chopping = channel.chopping_enabled; 416 | 417 | if channel.block_amount == 0 { 418 | channel.finish(); 419 | self.active_port = None; 420 | self.finish_set_interrupt(bus.intc(), port); 421 | } else { 422 | self.active_remaining = channel.block_size(); 423 | self.gap_ticks += 1; 424 | self.gap_started = chopping; 425 | } 426 | } 427 | } 428 | 429 | fn tick_linked_list(&mut self, port: DmacPort, bus: &mut Bus) { 430 | let channel = self.channel(port); 431 | let direction = channel.direction(); 432 | 433 | if direction != Direction::FromRam { 434 | panic!("[DMAC] [ERROR] Unsupported direction {:?} for linked list transfer", direction); 435 | } 436 | 437 | if port != DmacPort::GPU { 438 | panic!("[DMAC] [ERROR] Unsupported {:?} for linked list transfer", port); 439 | } 440 | 441 | if self.gap_ticks > 0 { 442 | self.gap_ticks += 1; 443 | return; 444 | } 445 | 446 | let header = LittleEndian::read_u32(&bus.ram()[self.active_address as usize..]); 447 | let payload_length = header >> 24; 448 | 449 | for _ in 0..payload_length { 450 | self.active_address = (self.active_address + 4) & 0x1f_fffc; 451 | 452 | let command = LittleEndian::read_u32(&bus.ram()[self.active_address as usize..]); 453 | bus.gpu_mut().gp0_write(command); 454 | } 455 | 456 | self.active_count += payload_length as usize; 457 | self.active_address = header & 0x1f_fffc; 458 | self.channel_mut(port).base_address = header & 0x1f_fffc; 459 | 460 | let chopping = channel.chopping_enabled; 461 | 462 | if (header & 0x80_0000) != 0 { 463 | self.channel_mut(port).finish(); 464 | 465 | self.active_port = None; 466 | self.finish_set_interrupt(bus.intc(), port); 467 | } else { 468 | self.gap_started = chopping; 469 | self.gap_ticks += 1; 470 | } 471 | } 472 | 473 | pub fn tick(&mut self, bus: &mut Bus) -> usize { 474 | let mut count = 0; 475 | 476 | if let Some(p) = self.active_port { 477 | let channel = self.channel(p); 478 | let sync = channel.sync(); 479 | 480 | if self.dma_enabled(p) { 481 | match sync { 482 | SyncMode::Manual => self.tick_manual(p, bus), 483 | SyncMode::Request => self.tick_request(p, bus), 484 | SyncMode::LinkedList => self.tick_linked_list(p, bus), 485 | }; 486 | } else { 487 | self.active_port = None; 488 | } 489 | 490 | count = self.active_count; 491 | self.active_count = 0; 492 | } 493 | 494 | count 495 | } 496 | 497 | pub fn active(&self) -> bool { 498 | self.active_port.is_some() 499 | } 500 | 501 | pub fn in_gap(&self) -> bool { 502 | self.gap_ticks > 0 503 | } 504 | 505 | pub fn gap_started(&mut self) -> bool { 506 | let gap_started = self.gap_started && self.gap_ticks > 32; 507 | 508 | if gap_started { 509 | self.gap_started = false; 510 | } 511 | 512 | gap_started 513 | } 514 | 515 | pub fn chopping_enabled(&self) -> bool { 516 | let channel = DmacPort::from(self.active_port.unwrap()); 517 | return self.channels[channel].chopping_enabled; 518 | } 519 | 520 | pub fn tick_gap(&mut self, ticks: usize) { 521 | self.gap_ticks -= ticks as isize; 522 | } 523 | 524 | pub fn channel(&self, port: DmacPort) -> DmacChannel { 525 | let channel = DmacPort::from(port); 526 | self.channels[channel] 527 | } 528 | 529 | pub fn channel_mut(&mut self, port: DmacPort) -> &mut DmacChannel { 530 | let channel = DmacPort::from(port); 531 | &mut self.channels[channel] 532 | } 533 | 534 | pub fn dma_enabled(&self, port: DmacPort) -> bool { 535 | let p = DmacPort::from(port); 536 | 537 | (self.control & (1 << ((p << 2) + 3))) != 0 538 | } 539 | 540 | pub fn finish_set_interrupt(&mut self, intc: &mut Intc, port: DmacPort) { 541 | let bit = DmacPort::from(port); 542 | 543 | let mask = 1 << (16 + bit); 544 | let status = 1 << (24 + bit); 545 | 546 | if self.interrupt & mask != 0 { 547 | self.interrupt |= status; 548 | } 549 | 550 | self.update_master_flag(intc); 551 | } 552 | 553 | fn update_master_flag(&mut self, intc: &mut Intc) { 554 | let prev_master = (self.interrupt & 0x8000_0000) != 0; 555 | 556 | let force = (self.interrupt & (1 << 15)) != 0; 557 | let master_enable = (self.interrupt & (1 << 23)) != 0; 558 | let flag = (self.interrupt & 0x7f00_0000) >> 24; 559 | let enable = (self.interrupt & 0x007f_0000) >> 16; 560 | 561 | let interrupt_enable = (flag & enable) != 0; 562 | 563 | self.interrupt &= !0x8000_0000; 564 | 565 | if force | (master_enable & interrupt_enable) { 566 | self.interrupt |= 0x8000_0000; 567 | 568 | if !prev_master { 569 | intc.assert_irq(Interrupt::Dma); 570 | } 571 | } 572 | } 573 | 574 | pub fn read(&self, address: u32) -> u32 { 575 | let section = (address as usize & 0x70) >> 4; 576 | let register = address & 0x0f; 577 | 578 | match section { 579 | 0..=5 => { 580 | let port = DmacPort::to(section); 581 | let channel = self.channel(port); 582 | 583 | match register { 584 | 0 => channel.base_address, 585 | 4 => channel.block_control_read(), 586 | 8 => channel.channel_control_read(), 587 | _ => panic!("[ERROR] [DMAC] Unknown DMA read 0x{:08x}", address), 588 | } 589 | } 590 | 6 => { 591 | let channel = self.channel(DmacPort::OTC); 592 | 593 | match register { 594 | 0 => channel.base_address, 595 | 4 => channel.block_control_read(), 596 | 8 => channel.channel_control_read() | 0x0000_0002, 597 | _ => panic!("[ERROR] [DMAC] Unknown DMA read 0x{:08x}", address), 598 | } 599 | } 600 | 7 => match register { 601 | 0 => self.control, 602 | 4 => self.interrupt, 603 | 6 => self.interrupt >> 16, 604 | _ => panic!("[ERROR] [DMAC] Unknown DMA read 0x{:08x}", address), 605 | }, 606 | _ => unreachable!(), 607 | } 608 | } 609 | 610 | pub fn write(&mut self, intc: &mut Intc, address: u32, value: u32) { 611 | let section = (address as usize & 0x70) >> 4; 612 | let register = address & 0x0f; 613 | 614 | let port = DmacPort::to(section); 615 | 616 | match section { 617 | 0..=5 => { 618 | let channel = self.channel_mut(port); 619 | 620 | match register { 621 | 0 => channel.base_address = value & 0xfffffc, 622 | 4 => channel.block_control_write(value), 623 | 8 => channel.channel_control_write(value), 624 | _ => panic!("[ERROR] [DMAC] Unknown DMA write 0x{:08x}", address), 625 | }; 626 | } 627 | 6 => { 628 | let channel = self.channel_mut(port); 629 | 630 | match register { 631 | 0 => channel.base_address = value & 0xfffffc, 632 | 4 => channel.block_control_write(value), 633 | 8 => channel.channel_control_write((value & 0x5100_0000) | 0x0000_0002), 634 | _ => panic!("[ERROR] [DMAC] Unknown DMA write 0x{:08x}", address), 635 | }; 636 | } 637 | 7 => match register { 638 | 0 => self.control = value, 639 | 4 => { 640 | self.interrupt &= 0xff00_0000; 641 | self.interrupt &= !(value & 0x7f00_0000); 642 | self.interrupt |= value & 0xff_803f; 643 | self.update_master_flag(intc); 644 | } 645 | 6 => { 646 | self.interrupt &= 0xff00_0000; 647 | self.interrupt &= !((value << 16) & 0x7f00_0000); 648 | self.interrupt |= (value << 16) & 0xff_0000; 649 | self.update_master_flag(intc); 650 | } 651 | _ => panic!("[ERROR] [DMAC] Unknown DMA write 0x{:08x}", address), 652 | }, 653 | _ => unreachable!(), 654 | }; 655 | 656 | if section == 7 { 657 | return; 658 | } 659 | 660 | let channel = self.channel(port); 661 | 662 | if channel.active() { 663 | self.active_port = Some(port); 664 | 665 | match channel.sync() { 666 | SyncMode::Manual => { 667 | self.active_address = channel.base_address() & 0x1f_fffc; 668 | self.active_remaining = channel.block_size(); 669 | } 670 | SyncMode::Request => { 671 | self.active_address = channel.base_address() & 0x1f_fffc; 672 | self.active_remaining = channel.block_size(); 673 | } 674 | SyncMode::LinkedList => { 675 | self.active_address = channel.base_address() & 0x1f_fffc; 676 | self.active_remaining = 1; 677 | } 678 | } 679 | 680 | self.active_count = 0; 681 | 682 | if self.active_remaining == 0 { 683 | self.active_port = None; 684 | } 685 | } 686 | } 687 | } 688 | -------------------------------------------------------------------------------- /src/psx/cpu/instruction.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub struct Instruction(pub u32); 3 | 4 | impl Instruction { 5 | pub fn opcode(self) -> usize { 6 | ((self.0 >> 26) & 0x3f) as usize 7 | } 8 | 9 | pub fn rs(self) -> usize { 10 | ((self.0 >> 21) & 0x1f) as usize 11 | } 12 | 13 | pub fn rt(self) -> usize { 14 | ((self.0 >> 16) & 0x1f) as usize 15 | } 16 | 17 | pub fn rd(self) -> usize { 18 | ((self.0 >> 11) & 0x1f) as usize 19 | } 20 | 21 | pub fn shift(self) -> usize { 22 | ((self.0 >> 6) & 0x1f) as usize 23 | } 24 | 25 | pub fn imm(self) -> u32 { 26 | (self.0 & 0xffff) as u32 27 | } 28 | 29 | pub fn imm_se(self) -> u32 { 30 | (self.0 & 0xffff) as i16 as u32 31 | } 32 | 33 | pub fn function(self) -> usize { 34 | (self.0 & 0x3f) as usize 35 | } 36 | 37 | pub fn target(self) -> u32 { 38 | self.0 & 0x3ff_ffff 39 | } 40 | } -------------------------------------------------------------------------------- /src/psx/exp2.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | const DUART_SRA: u32 = 0x1f802021; 6 | const DUART_THRA: u32 = 0x1f802023; 7 | 8 | const DUART_SR_TXRDY: u8 = 0x4; 9 | 10 | #[derive(Deserialize, Serialize)] 11 | pub struct Exp2 { 12 | tx_buf: Vec, 13 | } 14 | 15 | impl Exp2 { 16 | pub fn new() -> Exp2 { 17 | Exp2 { 18 | tx_buf: Vec::new(), 19 | } 20 | } 21 | 22 | fn tx_byte(&mut self, byte: u8) { 23 | if byte == 0xd { 24 | return; 25 | } 26 | 27 | if byte == 0xa { 28 | if self.tx_buf.len() != 0 { 29 | println!("{}", str::from_utf8(&self.tx_buf).unwrap()); 30 | self.tx_buf.clear(); 31 | } 32 | 33 | return; 34 | } 35 | 36 | self.tx_buf.push(byte); 37 | } 38 | 39 | pub fn read8(&mut self, address: u32) -> u8 { 40 | if address == DUART_SRA { 41 | return DUART_SR_TXRDY; 42 | } 43 | 44 | 0 45 | } 46 | 47 | pub fn write8(&mut self, address: u32, value: u8) { 48 | if address == DUART_THRA { 49 | self.tx_byte(value); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/psx/intc.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, PartialEq)] 4 | pub enum Interrupt { 5 | Vblank, 6 | Gpu, 7 | Cdrom, 8 | Dma, 9 | Tmr0, 10 | Tmr1, 11 | Tmr2, 12 | Controller, 13 | // Sio, 14 | Spu, 15 | // Pio, 16 | } 17 | 18 | impl Interrupt { 19 | pub fn to_u32(interrupt: &Interrupt) -> u32 { 20 | use self::Interrupt::*; 21 | 22 | match interrupt { 23 | Vblank => 0x1, 24 | Gpu => 0x2, 25 | Cdrom => 0x4, 26 | Dma => 0x8, 27 | Tmr0 => 0x10, 28 | Tmr1 => 0x20, 29 | Tmr2 => 0x40, 30 | Controller => 0x80, 31 | // Sio => 0x100, 32 | Spu => 0x200, 33 | // Pio => 0x400, 34 | } 35 | } 36 | } 37 | 38 | #[derive(Deserialize, Serialize)] 39 | struct InterruptRegister { 40 | pio: bool, 41 | spu: bool, 42 | sio: bool, 43 | controller: bool, 44 | tmr2: bool, 45 | tmr1: bool, 46 | tmr0: bool, 47 | dma: bool, 48 | cdrom: bool, 49 | gpu: bool, 50 | vblank: bool, 51 | } 52 | 53 | impl InterruptRegister { 54 | pub fn new() -> InterruptRegister { 55 | InterruptRegister { 56 | pio: false, 57 | spu: false, 58 | sio: false, 59 | controller: false, 60 | tmr2: false, 61 | tmr1: false, 62 | tmr0: false, 63 | dma: false, 64 | cdrom: false, 65 | gpu: false, 66 | vblank: false, 67 | } 68 | } 69 | 70 | pub fn read(&self) -> u32 { 71 | let mut value = 0; 72 | 73 | value |= (self.pio as u32) << 10; 74 | value |= (self.spu as u32) << 9; 75 | value |= (self.sio as u32) << 8; 76 | value |= (self.controller as u32) << 7; 77 | value |= (self.tmr2 as u32) << 6; 78 | value |= (self.tmr1 as u32) << 5; 79 | value |= (self.tmr0 as u32) << 4; 80 | value |= (self.dma as u32) << 3; 81 | value |= (self.cdrom as u32) << 2; 82 | value |= (self.gpu as u32) << 1; 83 | value |= (self.vblank as u32) << 0; 84 | 85 | value 86 | } 87 | 88 | pub fn write(&mut self, value: u32) { 89 | self.pio = (value & 0x400) != 0; 90 | self.spu = (value & 0x200) != 0; 91 | self.sio = (value & 0x100) != 0; 92 | self.controller = (value & 0x80) != 0; 93 | self.tmr2 = (value & 0x40) != 0; 94 | self.tmr1 = (value & 0x20) != 0; 95 | self.tmr0 = (value & 0x10) != 0; 96 | self.dma = (value & 0x8) != 0; 97 | self.cdrom = (value & 0x4) != 0; 98 | self.gpu = (value & 0x2) != 0; 99 | self.vblank = (value & 0x1) != 0; 100 | } 101 | } 102 | 103 | #[derive(Deserialize, Serialize)] 104 | pub struct Intc { 105 | status: InterruptRegister, 106 | mask: InterruptRegister, 107 | 108 | pending: bool, 109 | } 110 | 111 | impl Intc { 112 | pub fn new() -> Intc { 113 | Intc { 114 | status: InterruptRegister::new(), 115 | mask: InterruptRegister::new(), 116 | 117 | pending: false, 118 | } 119 | } 120 | 121 | pub fn pending(&self) -> bool { 122 | self.pending 123 | } 124 | 125 | fn update_pending(&mut self) { 126 | let status = self.status.read(); 127 | let mask = self.mask.read(); 128 | 129 | self.pending = (status & mask) != 0; 130 | } 131 | 132 | pub fn assert_irq(&mut self, interrupt: Interrupt) { 133 | let status = self.status.read(); 134 | self.status.write(status | Interrupt::to_u32(&interrupt)); 135 | 136 | self.update_pending(); 137 | } 138 | 139 | pub fn read_status(&self) -> u32 { 140 | self.status.read() 141 | } 142 | 143 | pub fn acknowledge_irq(&mut self, value: u32) { 144 | let status = self.status.read(); 145 | self.status.write(status & value); 146 | 147 | self.update_pending(); 148 | } 149 | 150 | pub fn read_mask(&self) -> u32 { 151 | self.mask.read() 152 | } 153 | 154 | pub fn write_mask(&mut self, value: u32) { 155 | self.mask.write(value); 156 | 157 | self.update_pending(); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/psx/mdec.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::mem; 3 | 4 | use serde::{Deserialize, Serialize}; 5 | use serde_big_array::BigArray; 6 | 7 | use crate::util; 8 | 9 | const MDEC_BLK_CR: usize = 0; 10 | const MDEC_BLK_CB: usize = 1; 11 | const MDEC_BLK_Y: usize = 2; 12 | 13 | const MDEC_QT_UV: usize = 0; 14 | const MDEC_QT_Y: usize = 1; 15 | 16 | const MDEC_ZAGZIG: [usize; 64] = [ 17 | 0, 1, 8, 16, 9, 2, 3, 10, 18 | 17, 24, 32, 25, 18, 11, 4, 5, 19 | 12, 19, 26, 33, 40, 48, 41, 34, 20 | 27, 20, 13, 6, 7, 14, 21, 28, 21 | 35, 42, 49, 56, 57, 50, 43, 36, 22 | 29, 22, 15, 23, 30, 37, 44, 51, 23 | 58, 59, 52, 45, 38, 31, 39, 46, 24 | 53, 60, 61, 54, 47, 55, 62, 63, 25 | ]; 26 | 27 | #[derive(Clone, Copy, Deserialize, Serialize)] 28 | struct QuantTable { 29 | #[serde(with = "BigArray")] 30 | data: [u8; 64], 31 | } 32 | 33 | impl QuantTable { 34 | pub fn new() -> QuantTable { 35 | QuantTable { 36 | data: [0; 64], 37 | } 38 | } 39 | } 40 | 41 | #[derive(Clone, Copy, Deserialize, Serialize)] 42 | struct Block { 43 | #[serde(with = "BigArray")] 44 | data: [i16; 64], 45 | } 46 | 47 | impl Block { 48 | pub fn new() -> Block { 49 | Block { 50 | data: [0; 64], 51 | } 52 | } 53 | } 54 | 55 | #[derive(Deserialize, Serialize)] 56 | pub struct Mdec { 57 | data_out: VecDeque, 58 | data_in: VecDeque, 59 | 60 | #[serde(with = "BigArray")] 61 | quant_tables: [QuantTable; 2], 62 | 63 | #[serde(with = "BigArray")] 64 | scale_table: [i16; 64], 65 | 66 | #[serde(with = "BigArray")] 67 | blocks: [Block; 3], 68 | 69 | processing_command: bool, 70 | command: usize, 71 | 72 | current_block: usize, 73 | 74 | words_remaining: u16, 75 | last_word_received: bool, 76 | 77 | dma0_enable: bool, 78 | dma1_enable: bool, 79 | 80 | output_depth: u32, 81 | output_signed: bool, 82 | output_bit15: bool, 83 | 84 | send_colour: bool, 85 | } 86 | 87 | impl Mdec { 88 | pub fn new() -> Mdec { 89 | Mdec { 90 | data_out: VecDeque::new(), 91 | data_in: VecDeque::new(), 92 | 93 | quant_tables: [QuantTable::new(); 2], 94 | scale_table: [0; 64], 95 | blocks: [Block::new(); 3], 96 | 97 | processing_command: false, 98 | command: 0, 99 | 100 | current_block: 0, 101 | 102 | words_remaining: 0, 103 | last_word_received: false, 104 | 105 | dma0_enable: false, 106 | dma1_enable: false, 107 | 108 | output_depth: 0, 109 | output_signed: false, 110 | output_bit15: false, 111 | 112 | send_colour: false, 113 | } 114 | } 115 | 116 | fn reset(&mut self) { 117 | self.processing_command = false; 118 | 119 | self.current_block = 4; 120 | self.words_remaining = 0; 121 | } 122 | 123 | fn decode_block(&mut self, blk: usize, qt: usize) -> bool { 124 | let quant = self.quant_tables[qt]; 125 | 126 | for i in 0..64 { 127 | self.blocks[blk].data[i] = 0; 128 | } 129 | 130 | if self.data_in.is_empty() { 131 | return false; 132 | } 133 | 134 | let mut data = self.data_in.pop_front().unwrap(); 135 | let mut k = 0; 136 | 137 | while data == 0xfe00 { 138 | if self.data_in.is_empty() { 139 | return false; 140 | } 141 | 142 | data = self.data_in.pop_front().unwrap(); 143 | } 144 | 145 | let quant_factor = data >> 10; 146 | let mut dc = (util::sign_extend_u16(data & 0x3ff, 10) as i16) * quant.data[k] as i16; 147 | 148 | loop { 149 | if quant_factor == 0 { 150 | dc = (util::sign_extend_u16(data & 0x3ff, 10) as i16) * 2; 151 | } 152 | 153 | dc = util::clip(dc, -0x400, 0x3ff); 154 | 155 | if quant_factor > 0 { 156 | self.blocks[blk].data[MDEC_ZAGZIG[k]] = dc; 157 | } else if quant_factor == 0 { 158 | self.blocks[blk].data[k] = dc; 159 | } 160 | 161 | if self.data_in.is_empty() { 162 | return false; 163 | } 164 | 165 | data = self.data_in.pop_front().unwrap(); 166 | 167 | k += (data >> 10) as usize + 1; 168 | 169 | if k <= 63 { 170 | dc = ((util::sign_extend_u16(data & 0x3ff, 10) as i16) * (quant.data[k] as i16) * (quant_factor as i16) + 4) >> 3; 171 | continue; 172 | } 173 | 174 | break; 175 | } 176 | 177 | self.idct(blk); 178 | 179 | return true; 180 | } 181 | 182 | fn idct(&mut self, blk: usize) { 183 | let src = &mut self.blocks[blk]; 184 | let dst = &mut [0; 64]; 185 | 186 | for _ in 0..2 { 187 | for x in 0..8 { 188 | for y in 0..8 { 189 | let mut sum = 0; 190 | 191 | for z in 0..8 { 192 | sum += (src.data[y + z * 8] as i32) * ((self.scale_table[x + z * 8] as i32) >> 3); 193 | } 194 | 195 | dst[x + y * 8] = ((sum + 0xfff) >> 13) as i16; 196 | } 197 | } 198 | 199 | mem::swap(&mut src.data, dst); 200 | } 201 | } 202 | 203 | fn yuv_to_rgb(&mut self, output: &mut [u8], xx: usize, yy: usize) { 204 | for y in 0..8 { 205 | for x in 0..8 { 206 | let mut r = self.blocks[MDEC_BLK_CR].data[((x + xx) >> 1) + ((y + yy) >> 1) * 8]; 207 | let mut b = self.blocks[MDEC_BLK_CB].data[((x + xx) >> 1) + ((y + yy) >> 1) * 8]; 208 | let mut g = ((-0.3437 * (b as f32)) + (-0.7143 * (r as f32))) as i16; 209 | 210 | r = (1.402 * (r as f32)) as i16; 211 | b = (1.772 * (b as f32)) as i16; 212 | 213 | let l = self.blocks[MDEC_BLK_Y].data[x + y * 8]; 214 | 215 | r = util::clip(l + r, -128, 127); 216 | g = util::clip(l + g, -128, 127); 217 | b = util::clip(l + b, -128, 127); 218 | 219 | if !self.output_signed { 220 | r ^= 0x80; 221 | g ^= 0x80; 222 | b ^= 0x80; 223 | } 224 | 225 | if self.output_depth == 3 { 226 | let r5bit = ((r as u8) >> 3) as u16; 227 | let g5bit = ((g as u8) >> 3) as u16; 228 | let b5bit = ((b as u8) >> 3) as u16; 229 | 230 | let mut data = (b5bit << 10) | (g5bit << 5) | r5bit; 231 | 232 | if self.output_bit15 { 233 | data |= 0x8000; 234 | } 235 | 236 | output[0 + ((x + xx) + (y + yy) * 16) * 2] = data as u8; 237 | output[1 + ((x + xx) + (y + yy) * 16) * 2] = (data >> 8) as u8; 238 | } else if self.output_depth == 2 { 239 | output[0 + ((x + xx) + (y + yy) * 16) * 3] = r as u8; 240 | output[1 + ((x + xx) + (y + yy) * 16) * 3] = g as u8; 241 | output[2 + ((x + xx) + (y + yy) * 16) * 3] = b as u8; 242 | } 243 | } 244 | } 245 | } 246 | 247 | fn process_command(&mut self, value: u32) { 248 | self.data_in.push_back(value as u16); 249 | self.data_in.push_back((value >> 16) as u16); 250 | self.words_remaining -= 1; 251 | 252 | let mut output = [0; 768]; 253 | 254 | if self.words_remaining == 0 { 255 | match self.command { 256 | 1 => { 257 | let mut finished; 258 | 259 | while self.data_in.len() != 0 { 260 | match self.current_block { 261 | 0 => { 262 | finished = self.decode_block(MDEC_BLK_Y, MDEC_QT_Y); 263 | self.yuv_to_rgb(&mut output, 0, 0); 264 | }, 265 | 1 => { 266 | finished = self.decode_block(MDEC_BLK_Y, MDEC_QT_Y); 267 | self.yuv_to_rgb(&mut output, 8, 0); 268 | }, 269 | 2 => { 270 | finished = self.decode_block(MDEC_BLK_Y, MDEC_QT_Y); 271 | self.yuv_to_rgb(&mut output, 0, 8); 272 | }, 273 | 3 => { 274 | finished = self.decode_block(MDEC_BLK_Y, MDEC_QT_Y); 275 | self.yuv_to_rgb(&mut output, 8, 8); 276 | 277 | if self.output_depth == 2 { 278 | for i in 0..768 { 279 | self.data_out.push_back(output[i]); 280 | } 281 | } else if self.output_depth == 3 { 282 | for i in 0..512 { 283 | self.data_out.push_back(output[i]); 284 | } 285 | } 286 | }, 287 | 4 => finished = self.decode_block(MDEC_BLK_CR, MDEC_QT_UV), 288 | 5 => finished = self.decode_block(MDEC_BLK_CB, MDEC_QT_UV), 289 | _ => unreachable!(), 290 | }; 291 | 292 | if finished { 293 | self.current_block += 1; 294 | 295 | if self.current_block >= 6 { 296 | self.current_block = 0; 297 | } 298 | } 299 | } 300 | } 301 | 2 => { 302 | for i in 0..32 { 303 | let half = self.data_in.pop_front().unwrap(); 304 | self.quant_tables[MDEC_QT_Y].data[i * 2] = half as u8; 305 | self.quant_tables[MDEC_QT_Y].data[i * 2 + 1] = (half >> 8) as u8; 306 | } 307 | 308 | if self.send_colour { 309 | for i in 0..32 { 310 | let half = self.data_in.pop_front().unwrap(); 311 | self.quant_tables[MDEC_QT_UV].data[i * 2] = half as u8; 312 | self.quant_tables[MDEC_QT_UV].data[i * 2 + 1] = (half >> 8) as u8; 313 | } 314 | } 315 | } 316 | 3 => { 317 | for i in 0..64 { 318 | let half = self.data_in.pop_front().unwrap(); 319 | self.scale_table[i] = half as i16; 320 | } 321 | } 322 | _ => println!("[MDEC] [ERROR] Unknown command: {}", self.command), 323 | } 324 | 325 | self.processing_command = false; 326 | self.last_word_received = true; 327 | } 328 | } 329 | 330 | pub fn read_data(&mut self) -> u32 { 331 | let b0 = self.data_out.pop_front().unwrap() as u32; 332 | let b1 = self.data_out.pop_front().unwrap() as u32; 333 | let b2 = self.data_out.pop_front().unwrap() as u32; 334 | let b3 = self.data_out.pop_front().unwrap() as u32; 335 | 336 | b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) 337 | } 338 | 339 | pub fn write_command(&mut self, value: u32) { 340 | if self.processing_command { 341 | self.process_command(value); 342 | return; 343 | } 344 | 345 | self.command = (value >> 29) as usize; 346 | self.processing_command = true; 347 | self.last_word_received = false; 348 | 349 | match self.command { 350 | 0 => { 351 | self.words_remaining = 0; 352 | self.processing_command = false; 353 | self.last_word_received = true; 354 | } 355 | 1 => { 356 | self.words_remaining = value as u16; 357 | self.output_depth = (value & 0x18000000) >> 27; 358 | self.output_signed = (value & 0x4000000) != 0; 359 | self.output_bit15 = (value & 0x2000000) != 0; 360 | } 361 | 2 => { 362 | self.send_colour = (value & 0x1) != 0; 363 | self.words_remaining = match self.send_colour { 364 | false => 16, 365 | true => 32, 366 | }; 367 | } 368 | 3 => { 369 | self.words_remaining = 32; 370 | } 371 | _ => println!("[MDEC] [ERROR] Unknown command: {}", self.command), 372 | }; 373 | } 374 | 375 | pub fn read_status(&self) -> u32 { 376 | let mut status = 0; 377 | 378 | status |= (self.data_out.is_empty() as u32) << 31; 379 | status |= ((!self.data_in.is_empty()) as u32) << 30; 380 | status |= (self.processing_command as u32) << 29; 381 | status |= self.output_depth << 25; 382 | status |= (self.output_signed as u32) << 24; 383 | status |= (self.output_bit15 as u32) << 23; 384 | status |= (self.current_block as u32) << 16; 385 | 386 | status |= (self.words_remaining - 1) as u32; 387 | 388 | status 389 | } 390 | 391 | pub fn write_control(&mut self, value: u32) { 392 | if (value & 0x80000000) != 0 { 393 | self.reset() 394 | } 395 | 396 | self.dma0_enable = (value & 0x40000000) != 0; 397 | self.dma1_enable = (value & 0x20000000) != 0; 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /src/psx/mod.rs: -------------------------------------------------------------------------------- 1 | mod adpcm; 2 | pub mod bus; 3 | mod cdrom; 4 | pub mod cpu; 5 | mod exp2; 6 | mod gpu; 7 | mod intc; 8 | mod mdec; 9 | pub mod rasteriser; 10 | mod sio0; 11 | mod spu; 12 | mod timekeeper; 13 | mod timers; 14 | 15 | use std::fs::File; 16 | use std::io; 17 | 18 | use serde::{Deserialize, Serialize}; 19 | 20 | use crate::util; 21 | 22 | use self::bus::Bus; 23 | use self::cpu::R3000A; 24 | use self::sio0::controller::Controller; 25 | use self::timekeeper::Timekeeper; 26 | 27 | #[derive(Deserialize, Serialize)] 28 | pub struct System { 29 | pub running: bool, 30 | 31 | bus: Bus, 32 | cpu: R3000A, 33 | 34 | timekeeper: Timekeeper, 35 | 36 | bios_filepath: String, 37 | game_filepath: String, 38 | } 39 | 40 | impl System { 41 | pub fn new(bios_filepath: String, game_filepath: String) -> System { 42 | System { 43 | running: true, 44 | 45 | bus: Bus::new(bios_filepath.as_str(), game_filepath.as_str()), 46 | cpu: R3000A::new(), 47 | 48 | timekeeper: Timekeeper::new(), 49 | 50 | bios_filepath: bios_filepath, 51 | game_filepath: game_filepath, 52 | } 53 | } 54 | 55 | pub fn reset(&mut self) { 56 | self.bus.reset(); 57 | self.cpu.reset(); 58 | 59 | self.timekeeper.reset(); 60 | } 61 | 62 | pub fn reload_host_files(&mut self) { 63 | self.bus.cdrom().load_disc(&self.game_filepath); 64 | self.bus.sio0().load_memcards(); 65 | } 66 | 67 | pub fn run_frame(&mut self) { 68 | while !self.bus.gpu_mut().frame_complete() { 69 | while self.timekeeper.elapsed() < 128 { 70 | self.cpu.run(&mut self.bus, &mut self.timekeeper); 71 | } 72 | 73 | self.timekeeper.sync_all(&mut self.bus); 74 | } 75 | 76 | self.bus.sio0().sync(); 77 | } 78 | 79 | #[allow(dead_code)] 80 | pub fn load_psexe(&mut self, filename: String) -> io::Result<()> { 81 | let mut file = File::open(filename)?; 82 | 83 | util::discard(&mut file, 0x10)?; 84 | 85 | self.cpu.pc = util::read_u32(&mut file)?; 86 | self.cpu.new_pc = self.cpu.pc + 4; 87 | 88 | self.cpu.regs[28] = util::read_u32(&mut file)?; 89 | 90 | let file_dest = util::read_u32(&mut file)? as usize; 91 | let file_size = util::read_u32(&mut file)? as usize; 92 | 93 | util::discard(&mut file, 0x10)?; 94 | 95 | self.cpu.regs[29] = util::read_u32(&mut file)? + util::read_u32(&mut file)?; 96 | self.cpu.regs[30] = self.cpu.regs[29]; 97 | 98 | util::discard(&mut file, 0x7c8)?; 99 | 100 | let ram = self.bus.ram(); 101 | 102 | for i in 0..file_size { 103 | ram[(file_dest + i) & 0x1fffff] = util::read_u8(&mut file)?; 104 | } 105 | 106 | Ok(()) 107 | } 108 | 109 | pub fn get_audio_samples(&mut self) -> Vec { 110 | self.bus.spu().drain_samples() 111 | } 112 | 113 | pub fn get_controller(&mut self) -> &mut Controller { 114 | self.bus.sio0().controller() 115 | } 116 | 117 | pub fn get_disc_id(&mut self) -> String { 118 | self.bus.cdrom().get_disc_id() 119 | } 120 | 121 | pub fn get_disc_id_raw(&mut self) -> String { 122 | self.bus.cdrom().get_disc_id_raw() 123 | } 124 | 125 | #[allow(dead_code)] 126 | pub fn get_display_origin(&self) -> (u32, u32) { 127 | self.bus.gpu().get_display_origin() 128 | } 129 | 130 | pub fn get_display_size(&self) -> (u32, u32) { 131 | self.bus.gpu().get_display_size() 132 | } 133 | 134 | pub fn get_framebuffer(&self, 135 | data: &mut [u8], 136 | draw_full_vram: bool) { 137 | self.bus.gpu().get_framebuffer(data, draw_full_vram) 138 | } 139 | 140 | #[allow(dead_code)] 141 | pub fn dump_vram(&self) { 142 | self.bus.gpu().dump_vram(); 143 | } 144 | } -------------------------------------------------------------------------------- /src/psx/rasteriser/colour.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Clone, Copy, Deserialize, Serialize)] 4 | pub struct Colour { 5 | pub r: u8, 6 | pub g: u8, 7 | pub b: u8, 8 | pub a: bool, 9 | } 10 | 11 | impl Colour { 12 | pub fn new(r: u8, g: u8, b: u8, a: bool) -> Colour { 13 | Colour { r, g, b, a } 14 | } 15 | 16 | pub fn from_u16(colour: u16) -> Colour { 17 | let rb = (colour & 0x1f) as u8; 18 | let gb = ((colour >> 5) & 0x1f) as u8; 19 | let bb = ((colour >> 10) & 0x1f) as u8; 20 | 21 | let r = (rb << 3) | (rb >> 2); 22 | let g = (gb << 3) | (gb >> 2); 23 | let b = (bb << 3) | (bb >> 2); 24 | let a = (colour >> 15) != 0; 25 | 26 | Colour::new(r, g, b, a) 27 | } 28 | 29 | pub fn from_u32(colour: u32) -> Colour { 30 | let r = colour as u8; 31 | let g = (colour >> 8) as u8; 32 | let b = (colour >> 16) as u8; 33 | 34 | Colour::new(r, g, b, false) 35 | } 36 | 37 | pub fn to_u16(self) -> u16 { 38 | let mut pixel = 0; 39 | 40 | pixel |= ((self.r as u16) & 0xf8) >> 3; 41 | pixel |= ((self.g as u16) & 0xf8) << 2; 42 | pixel |= ((self.b as u16) & 0xf8) << 7; 43 | pixel |= (self.a as u16) << 15; 44 | 45 | pixel 46 | } 47 | 48 | pub fn r(self) -> i32 { 49 | self.r as i32 50 | } 51 | 52 | pub fn g(self) -> i32 { 53 | self.g as i32 54 | } 55 | 56 | pub fn b(self) -> i32 { 57 | self.b as i32 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/psx/rasteriser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod colour; 2 | pub mod vector2i; 3 | pub mod vector3i; 4 | 5 | pub use self::colour::Colour; 6 | pub use self::vector2i::Vector2i; 7 | pub use self::vector3i::Vector3i; -------------------------------------------------------------------------------- /src/psx/rasteriser/vector2i.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize ,Serialize}; 2 | 3 | #[derive(Clone, Copy, Deserialize, Serialize)] 4 | pub struct Vector2i { 5 | pub x: i32, 6 | pub y: i32, 7 | } 8 | 9 | impl Vector2i { 10 | pub fn new(x: i32, y: i32) -> Vector2i { 11 | Vector2i { x, y } 12 | } 13 | 14 | pub fn orient2d(a: Vector2i, b: Vector2i, c: Vector2i) -> i32 { 15 | (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/psx/rasteriser/vector3i.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub struct Vector3i { 3 | pub x: i32, 4 | pub y: i32, 5 | pub z: i32, 6 | } 7 | 8 | impl Vector3i { 9 | pub fn new(x: i32, y: i32, z: i32) -> Vector3i { 10 | Vector3i { x, y, z } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/psx/sio0/controller.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize)] 4 | 5 | pub struct Controller { 6 | state: usize, 7 | 8 | pub digital_mode: bool, 9 | 10 | pub button_select: bool, 11 | pub button_l3: bool, 12 | pub button_r3: bool, 13 | pub button_start: bool, 14 | pub button_dpad_up: bool, 15 | pub button_dpad_right: bool, 16 | pub button_dpad_down: bool, 17 | pub button_dpad_left: bool, 18 | pub button_l2: bool, 19 | pub button_r2: bool, 20 | pub button_l1: bool, 21 | pub button_r1: bool, 22 | pub button_triangle: bool, 23 | pub button_circle: bool, 24 | pub button_cross: bool, 25 | pub button_square: bool, 26 | 27 | pub axis_lx: u8, 28 | pub axis_ly: u8, 29 | pub axis_rx: u8, 30 | pub axis_ry: u8, 31 | } 32 | 33 | impl Controller { 34 | pub fn new() -> Controller { 35 | Controller { 36 | state: 0, 37 | digital_mode: false, 38 | 39 | button_select: false, 40 | button_l3: false, 41 | button_r3: false, 42 | button_start: false, 43 | button_dpad_up: false, 44 | button_dpad_right: false, 45 | button_dpad_down: false, 46 | button_dpad_left: false, 47 | button_l2: false, 48 | button_r2: false, 49 | button_l1: false, 50 | button_r1: false, 51 | button_triangle: false, 52 | button_circle: false, 53 | button_cross: false, 54 | button_square: false, 55 | 56 | axis_lx: 128, 57 | axis_ly: 128, 58 | axis_rx: 128, 59 | axis_ry: 128, 60 | } 61 | } 62 | 63 | pub fn reset_device_state(&mut self) { 64 | self.state = 0; 65 | } 66 | 67 | pub fn reset_switch_state(&mut self) { 68 | self.digital_mode = false; 69 | self.button_select = false; 70 | self.button_l3 = false; 71 | self.button_r3 = false; 72 | self.button_start = false; 73 | self.button_dpad_up = false; 74 | self.button_dpad_right = false; 75 | self.button_dpad_down = false; 76 | self.button_dpad_left = false; 77 | self.button_l2 = false; 78 | self.button_r2 = false; 79 | self.button_l1 = false; 80 | self.button_r1 = false; 81 | self.button_triangle = false; 82 | self.button_circle = false; 83 | self.button_cross = false; 84 | self.button_square = false; 85 | self.axis_lx = 128; 86 | self.axis_ly = 128; 87 | self.axis_rx = 128; 88 | self.axis_ry = 128; 89 | } 90 | 91 | pub fn response(&mut self, command: u8) -> u8 { 92 | let mut reply = 0xff; 93 | 94 | match self.state { 95 | 0 => self.state = 1, 96 | 1 => { 97 | if command == 0x42 { 98 | self.state = 2; 99 | reply = if self.digital_mode { 0x41 } else { 0x73 }; 100 | } else { 101 | self.state = 0; 102 | } 103 | }, 104 | 2 => { 105 | reply = 0x5a; 106 | self.state = 3; 107 | }, 108 | 3 => { 109 | reply = self.get_switch_state_lo(); 110 | self.state = 4; 111 | }, 112 | 4 => { 113 | reply = self.get_switch_state_hi(); 114 | self.state = if self.digital_mode { 0 } else { 5 }; 115 | }, 116 | 5 => { 117 | reply = self.axis_rx; 118 | self.state = 6; 119 | }, 120 | 6 => { 121 | reply = self.axis_ry; 122 | self.state = 7; 123 | }, 124 | 7 => { 125 | reply = self.axis_lx; 126 | self.state = 8; 127 | }, 128 | 8 => { 129 | reply = self.axis_ly; 130 | self.state = 0; 131 | }, 132 | _ => panic!("[CONTROLLER] [ERROR] Unknown state: {}", self.state), 133 | }; 134 | 135 | reply 136 | } 137 | 138 | pub fn ack(&self) -> bool { 139 | self.state != 0 140 | } 141 | 142 | pub fn enable(&self) -> bool { 143 | self.state != 0 144 | } 145 | 146 | fn get_switch_state_hi(&self) -> u8 { 147 | let mut value = 0; 148 | 149 | value |= (self.button_square as u8) << 7; 150 | value |= (self.button_cross as u8) << 6; 151 | value |= (self.button_circle as u8) << 5; 152 | value |= (self.button_triangle as u8) << 4; 153 | value |= (self.button_r1 as u8) << 3; 154 | value |= (self.button_l1 as u8) << 2; 155 | value |= (self.button_r2 as u8) << 1; 156 | value |= (self.button_l2 as u8) << 0; 157 | 158 | !value 159 | } 160 | 161 | fn get_switch_state_lo(&self) -> u8 { 162 | let mut value = 0; 163 | 164 | value |= (self.button_dpad_left as u8) << 7; 165 | value |= (self.button_dpad_down as u8) << 6; 166 | value |= (self.button_dpad_right as u8) << 5; 167 | value |= (self.button_dpad_up as u8) << 4; 168 | value |= (self.button_start as u8) << 3; 169 | value |= (self.button_r3 as u8) << 2; 170 | value |= (self.button_l3 as u8) << 1; 171 | value |= self.button_select as u8; 172 | 173 | !value 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/psx/sio0/memory_card.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::io::prelude::{Read, Write}; 3 | use std::os::windows::prelude::FileExt; 4 | use std::path; 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | pub const MEMORY_CARD_SIZE: usize = 0x20000; 9 | 10 | #[derive(Deserialize, Serialize)] 11 | 12 | pub struct MemoryCard { 13 | #[serde(skip)] 14 | file: Option, 15 | cache: Box<[u8]>, 16 | dirty: bool, 17 | 18 | ack: bool, 19 | state: usize, 20 | 21 | sector: u16, 22 | sector_counter: usize, 23 | 24 | flag: u8, 25 | previous: u8, 26 | 27 | checksum: u8, 28 | checksum_match: bool, 29 | } 30 | 31 | impl MemoryCard { 32 | pub fn new(filepath: &'static str) -> MemoryCard { 33 | let mut card = MemoryCard { 34 | file: Some(MemoryCard::get_card_file(filepath)), 35 | cache: vec![0; MEMORY_CARD_SIZE].into_boxed_slice(), 36 | dirty: false, 37 | 38 | ack: false, 39 | state: 0, 40 | 41 | sector: 0, 42 | sector_counter: 0, 43 | 44 | flag: 0, 45 | previous: 0, 46 | 47 | checksum: 0, 48 | checksum_match: false, 49 | }; 50 | 51 | card.load_cache(); 52 | card 53 | } 54 | 55 | pub fn reset(&mut self) { 56 | self.flag = 0x08; 57 | } 58 | 59 | pub fn reset_device_state(&mut self) { 60 | self.ack = false; 61 | self.state = 0; 62 | } 63 | 64 | fn get_card_file(filepath: &str) -> fs::File { 65 | let path = path::Path::new(filepath); 66 | 67 | // This should only be None if the path is at the root right? 68 | if let Some(parent) = path.parent() { 69 | fs::create_dir_all(parent).expect("unable to create path to memory card file"); 70 | } 71 | 72 | fs::OpenOptions::new() 73 | .read(true) 74 | .write(true) 75 | .create(true) 76 | .open(filepath) 77 | .expect("unable to create/open memory card file") 78 | } 79 | 80 | pub fn load(&mut self, filepath: &str) { 81 | self.file = Some(MemoryCard::get_card_file(filepath)); 82 | } 83 | 84 | fn load_cache(&mut self) { 85 | let file = self.file.as_mut().expect("no memcard file"); 86 | let metadata = file.metadata().unwrap(); 87 | 88 | // Forgo loading card data if file is smaller than expected (presumably empty) 89 | if metadata.len() >= MEMORY_CARD_SIZE as u64 { 90 | file.read_exact(&mut self.cache).unwrap(); 91 | self.dirty = false; 92 | } 93 | } 94 | 95 | fn flush_cache(&mut self) { 96 | let file = self.file.as_mut().expect("no memcard file"); 97 | file.seek_write(&self.cache, 0).unwrap(); 98 | file.flush().unwrap(); 99 | self.dirty = false; 100 | } 101 | 102 | fn read_cache(&self, index: usize) -> u8 { 103 | self.cache[index] 104 | } 105 | 106 | fn write_cache(&mut self, index: usize, value: u8) { 107 | self.cache[index] = value; 108 | self.dirty = true; 109 | } 110 | 111 | pub fn sync(&mut self) { 112 | if self.dirty { 113 | self.flush_cache(); 114 | } 115 | } 116 | 117 | pub fn response(&mut self, command: u8) -> u8 { 118 | self.ack = true; 119 | let mut reply = 0xff; 120 | 121 | match self.state { 122 | 0 => self.state = 1, 123 | 1 => { 124 | reply = self.flag; 125 | self.sector_counter = 0; 126 | 127 | match command { 128 | 0x52 => self.state = 10, 129 | 0x53 => self.state = 2, 130 | 0x57 => self.state = 21, 131 | _ => { 132 | println!("[MCD] [WARN] Unrecognised command: {:#x}", command); 133 | 134 | self.state = 0; 135 | self.ack = false; 136 | }, 137 | }; 138 | }, 139 | 2 => { 140 | reply = 0x5a; 141 | self.state = 3; 142 | }, 143 | 3 => { 144 | reply = 0x5d; 145 | self.state = 4; 146 | }, 147 | 4 => { 148 | reply = 0x5c; 149 | self.state = 5; 150 | }, 151 | 5 => { 152 | reply = 0x5d; 153 | self.state = 6; 154 | }, 155 | 6 => { 156 | reply = 0x04; 157 | self.state = 7; 158 | }, 159 | 7 => { 160 | reply = 0x00; 161 | self.state = 8; 162 | }, 163 | 8 => { 164 | reply = 0x00; 165 | self.state = 9; 166 | }, 167 | 9 => { 168 | reply = 0x80; 169 | self.state = 0; 170 | }, 171 | 10 => { 172 | reply = 0x5a; 173 | self.state = 11; 174 | }, 175 | 11 => { 176 | reply = 0x5d; 177 | self.state = 12; 178 | }, 179 | 12 => { 180 | reply = 0x00; 181 | self.sector &= 0xff; 182 | self.sector |= (command as u16) << 8; 183 | 184 | self.previous = command; 185 | self.checksum = command; 186 | 187 | self.state = 13; 188 | }, 189 | 13 => { 190 | reply = self.previous; 191 | 192 | self.sector &= 0xff00; 193 | self.sector |= command as u16; 194 | 195 | self.checksum ^= command; 196 | 197 | //println!("[MCD] [INFO] [R] Set sector to: {:#x}", self.sector); 198 | 199 | if self.sector > 0x3ff { 200 | self.sector = 0xffff; 201 | } 202 | 203 | self.state = 14; 204 | }, 205 | 14 => { 206 | reply = 0x5c; 207 | self.state = 15; 208 | }, 209 | 15 => { 210 | reply = 0x5d; 211 | self.state = 16; 212 | }, 213 | 16 => { 214 | reply = (self.sector >> 8) as u8; 215 | self.state = 17; 216 | }, 217 | 17 => { 218 | reply = self.sector as u8; 219 | 220 | //println!("[MCD] [INFO] Reading sector: {:#x}", self.sector); 221 | 222 | if self.sector == 0xffff { 223 | self.state = 0; 224 | self.ack = false; 225 | } else { 226 | self.state = 18; 227 | } 228 | }, 229 | 18 => { 230 | let sector_addr = (self.sector as usize) * 0x80; 231 | reply = self.read_cache(sector_addr + self.sector_counter); 232 | 233 | self.checksum ^= reply; 234 | 235 | self.sector_counter += 1; 236 | 237 | if self.sector_counter == 0x80 { 238 | self.state = 19; 239 | } 240 | }, 241 | 19 => { 242 | reply = self.checksum; 243 | self.state = 20; 244 | }, 245 | 20 => { 246 | //println!("[MCD] [INFO] [R] finishing transfer"); 247 | reply = 0x47; 248 | self.state = 0; 249 | self.ack = false; 250 | }, 251 | 21 => { 252 | self.flag &= !0x08; 253 | 254 | reply = 0x5a; 255 | self.state = 22; 256 | }, 257 | 22 => { 258 | reply = 0x5d; 259 | self.state = 23; 260 | }, 261 | 23 => { 262 | reply = 0x00; 263 | self.sector &= 0xff; 264 | self.sector |= (command as u16) << 8; 265 | 266 | self.previous = command; 267 | self.checksum = command; 268 | 269 | self.state = 24; 270 | }, 271 | 24 => { 272 | reply = self.previous; 273 | 274 | self.sector &= 0xff00; 275 | self.sector |= command as u16; 276 | 277 | self.previous = command; 278 | self.checksum ^= command; 279 | 280 | //println!("[MCD] [INFO] [W] Set sector to: {:#x}", self.sector); 281 | 282 | if self.sector > 0x3ff { 283 | self.state = 0; 284 | self.ack = false; 285 | } else { 286 | self.state = 25; 287 | } 288 | }, 289 | 25 => { 290 | reply = self.previous; 291 | 292 | let sector_addr = (self.sector as usize) * 0x80; 293 | self.write_cache(sector_addr + self.sector_counter, command); 294 | 295 | self.previous = command; 296 | self.checksum ^= command; 297 | 298 | self.sector_counter += 1; 299 | 300 | if self.sector_counter == 0x80 { 301 | self.state = 26; 302 | } 303 | }, 304 | 26 => { 305 | reply = self.previous; 306 | 307 | //println!("[MCD] [INFO] Written sector: {:#x}", self.sector); 308 | self.sync(); 309 | 310 | self.checksum_match = self.checksum == command; 311 | self.state = 27; 312 | }, 313 | 27 => { 314 | reply = 0x5c; 315 | self.state = 28; 316 | }, 317 | 28 => { 318 | reply = 0x5d; 319 | self.state = 29; 320 | }, 321 | 29 => { 322 | if self.checksum_match { 323 | reply = 0x47; 324 | } else { 325 | println!("[MCD] [WARN] Checksum mismatch: {:#x}", self.checksum); 326 | reply = 0x4e; 327 | } 328 | 329 | self.ack = false; 330 | self.state = 0; 331 | }, 332 | _ => panic!("[MCD] [ERROR] Unknown state: {}", self.state), 333 | }; 334 | 335 | reply 336 | } 337 | 338 | pub fn ack(&self) -> bool { 339 | self.ack 340 | } 341 | 342 | pub fn enable(&self) -> bool { 343 | self.state != 0 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/psx/sio0/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod controller; 2 | mod memory_card; 3 | 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use self::controller::Controller; 7 | use self::memory_card::MemoryCard; 8 | use super::intc::{Intc, Interrupt}; 9 | 10 | use crate::queue::Queue; 11 | 12 | #[derive(Deserialize, Serialize)] 13 | 14 | struct Mode { 15 | clk_output_polarity: bool, 16 | parity_type: bool, 17 | parity_enable: bool, 18 | baud_reload_factor: usize, 19 | } 20 | 21 | impl Mode { 22 | pub fn new() -> Mode { 23 | Mode { 24 | clk_output_polarity: false, 25 | parity_type: false, 26 | parity_enable: false, 27 | baud_reload_factor: 1, 28 | } 29 | } 30 | 31 | pub fn write(&mut self, value: u16) { 32 | self.clk_output_polarity = (value & 0x100) != 0; 33 | self.parity_type = (value & 0x20) != 0; 34 | self.parity_enable = (value & 0x10) != 0; 35 | self.baud_reload_factor = match value & 0x3 { 36 | 0 => 1, 37 | 1 => 1, 38 | 2 => 16, 39 | 3 => 64, 40 | _ => unreachable!(), 41 | }; 42 | } 43 | } 44 | 45 | #[derive(Deserialize, Serialize)] 46 | 47 | struct Control { 48 | slot: bool, 49 | ack_interrupt_enable: bool, 50 | rx_interrupt_enable: bool, 51 | tx_interrupt_enable: bool, 52 | rx_interrupt_count: usize, 53 | rx_enable: bool, 54 | joy_n_output: bool, 55 | tx_enable: bool, 56 | } 57 | 58 | impl Control { 59 | pub fn new() -> Control { 60 | Control { 61 | slot: false, 62 | ack_interrupt_enable: false, 63 | rx_interrupt_enable: false, 64 | tx_interrupt_enable: false, 65 | rx_interrupt_count: 1, 66 | rx_enable: false, 67 | joy_n_output: false, 68 | tx_enable: false, 69 | } 70 | } 71 | 72 | pub fn read(&self) -> u16 { 73 | let mut value = 0; 74 | 75 | value |= (self.slot as u16) << 13; 76 | value |= (self.ack_interrupt_enable as u16) << 12; 77 | value |= (self.rx_interrupt_enable as u16) << 11; 78 | value |= (self.tx_interrupt_enable as u16) << 10; 79 | value |= match self.rx_interrupt_count { 80 | 1 => 0, 81 | 2 => 1, 82 | 4 => 2, 83 | 8 => 3, 84 | _ => unreachable!(), 85 | } << 8; 86 | value |= (self.rx_enable as u16) << 2; 87 | value |= (self.joy_n_output as u16) << 1; 88 | value |= self.tx_enable as u16; 89 | 90 | value 91 | } 92 | 93 | pub fn write(&mut self, value: u16) { 94 | self.slot = (value & 0x2000) != 0; 95 | self.ack_interrupt_enable = (value & 0x1000) != 0; 96 | self.rx_interrupt_enable = (value & 0x800) != 0; 97 | self.tx_interrupt_enable = (value & 0x400) != 0; 98 | self.rx_interrupt_count = 1 << ((value & 0x300) >> 8); 99 | self.rx_enable = (value & 0x4) != 0; 100 | self.joy_n_output = (value & 0x2) != 0; 101 | self.tx_enable = (value & 0x1) != 0; 102 | } 103 | } 104 | 105 | #[derive(Deserialize, PartialEq, Serialize)] 106 | enum Device { 107 | None, 108 | Controller, 109 | MemoryCard, 110 | } 111 | 112 | #[derive(Deserialize, Serialize)] 113 | pub struct Sio0 { 114 | controller: Controller, 115 | mem_card1: MemoryCard, 116 | 117 | active_device: Device, 118 | 119 | baudrate: usize, 120 | ticks_left: isize, 121 | 122 | in_transfer: bool, 123 | in_acknowledge: bool, 124 | 125 | interrupt_request: bool, 126 | ack_input_level: bool, 127 | rx_parity_error: bool, 128 | tx_ready_2: bool, 129 | tx_ready_1: bool, 130 | 131 | mode: Mode, 132 | control: Control, 133 | 134 | rx_fifo: Queue, 135 | tx_fifo: Queue, 136 | } 137 | 138 | impl Sio0 { 139 | pub fn new() -> Sio0 { 140 | Sio0 { 141 | controller: Controller::new(), 142 | mem_card1: MemoryCard::new("./cards/card1.mcd"), 143 | 144 | active_device: Device::None, 145 | 146 | baudrate: 0, 147 | ticks_left: 0, 148 | 149 | in_transfer: false, 150 | in_acknowledge: false, 151 | 152 | interrupt_request: false, 153 | ack_input_level: false, 154 | rx_parity_error: false, 155 | tx_ready_2: false, 156 | tx_ready_1: false, 157 | 158 | mode: Mode::new(), 159 | control: Control::new(), 160 | 161 | rx_fifo: Queue::::new(8), 162 | tx_fifo: Queue::::new(1), 163 | } 164 | } 165 | 166 | pub fn reset(&mut self) { 167 | self.mem_card1.reset(); 168 | } 169 | 170 | pub fn reset_device_states(&mut self) { 171 | // println!("[SIO] resetting device states"); 172 | self.active_device = Device::None; 173 | self.in_transfer = false; 174 | self.in_acknowledge = false; 175 | self.controller.reset_device_state(); 176 | self.mem_card1.reset_device_state(); 177 | } 178 | 179 | pub fn load_memcards(&mut self) { 180 | self.mem_card1.load("./cards/card1.mcd"); 181 | } 182 | 183 | pub fn tick(&mut self, intc: &mut Intc, clocks: usize) { 184 | if self.in_transfer { 185 | self.ticks_left -= clocks as isize; 186 | 187 | if self.ticks_left > 0 { 188 | return; 189 | } 190 | 191 | self.in_transfer = false; 192 | 193 | let command = self.tx_fifo.pop(); 194 | 195 | if self.control.slot { 196 | self.rx_fifo.push(0xff); 197 | return; 198 | } 199 | 200 | if self.active_device == Device::None { 201 | if command == 0x01 { 202 | self.active_device = Device::Controller; 203 | } else if command == 0x81 { 204 | self.active_device = Device::MemoryCard; 205 | } 206 | } 207 | 208 | let mut response = 0xff; 209 | let mut ack = false; 210 | let mut enable = false; 211 | 212 | if self.active_device == Device::Controller { 213 | response = self.controller.response(command); 214 | ack = self.controller.ack(); 215 | enable = self.controller.enable(); 216 | 217 | if ack { 218 | self.ticks_left += 338; 219 | self.in_acknowledge = true; 220 | } 221 | } else if self.active_device == Device::MemoryCard { 222 | response = self.mem_card1.response(command); 223 | ack = self.mem_card1.ack(); 224 | enable = self.mem_card1.enable(); 225 | 226 | if ack { 227 | self.ticks_left += 338; 228 | self.in_acknowledge = true; 229 | } 230 | } 231 | 232 | if !enable { 233 | self.active_device = Device::None; 234 | } 235 | 236 | self.rx_fifo.push(response); 237 | 238 | self.ack_input_level = ack; 239 | self.tx_ready_2 = true; 240 | } else if self.in_acknowledge { 241 | self.ticks_left -= clocks as isize; 242 | 243 | if self.ticks_left < 0 { 244 | self.in_acknowledge = false; 245 | self.ack_input_level = false; 246 | intc.assert_irq(Interrupt::Controller); 247 | } 248 | } 249 | } 250 | 251 | pub fn controller(&mut self) -> &mut Controller { 252 | &mut self.controller 253 | } 254 | 255 | pub fn sync(&mut self) { 256 | self.mem_card1.sync(); 257 | } 258 | 259 | pub fn rx_data(&mut self) -> u32 { 260 | let data = self.rx_fifo.pop(); 261 | // println!("[SIO0] rx <- {:02X}", data); 262 | 263 | data as u32 264 | } 265 | 266 | pub fn tx_data(&mut self, value: u32) { 267 | self.tx_fifo.push(value as u8); 268 | // println!("[SIO0] tx -> {:02X}", value as u8); 269 | 270 | self.tx_ready_1 = true; 271 | self.tx_ready_2 = false; 272 | 273 | assert!(!self.in_transfer); 274 | assert!(!self.in_acknowledge); 275 | 276 | self.ticks_left = (self.baudrate as isize & !1) * 8; 277 | self.in_transfer = true; 278 | } 279 | 280 | pub fn status(&mut self) -> u32 { 281 | let mut value = 0; 282 | 283 | value |= (self.baudrate as u32) << 11; 284 | value |= (self.interrupt_request as u32) << 9; 285 | value |= (self.ack_input_level as u32) << 7; 286 | value |= (self.rx_parity_error as u32) << 3; 287 | value |= (self.tx_ready_2 as u32) << 2; 288 | value |= (self.rx_fifo.has_data() as u32) << 1; 289 | value |= self.tx_ready_1 as u32; 290 | 291 | value 292 | } 293 | 294 | pub fn write_mode(&mut self, value: u16) { 295 | self.mode.write(value); 296 | } 297 | 298 | pub fn read_control(&self) -> u32 { 299 | self.control.read() as u32 300 | } 301 | 302 | pub fn write_control(&mut self, value: u16) { 303 | self.control.write(value); 304 | 305 | if !self.control.joy_n_output { 306 | self.reset_device_states(); 307 | } 308 | 309 | if (value & 0x40) != 0 { 310 | self.write_mode(0); 311 | self.write_control(0); 312 | self.write_baud(0); 313 | 314 | self.rx_fifo.clear(); 315 | self.tx_fifo.clear(); 316 | 317 | self.tx_ready_1 = true; 318 | self.tx_ready_2 = true; 319 | } 320 | 321 | if ((value & 0x10) != 0) && !self.ack_input_level { 322 | self.interrupt_request = false; 323 | self.rx_parity_error = false; 324 | } 325 | } 326 | 327 | pub fn write_baud(&mut self, value: u16) { 328 | self.baudrate = value as usize; 329 | } 330 | 331 | pub fn read_baud(&self) -> u32 { 332 | self.baudrate as u32 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/psx/spu/adsr.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::util::clip; 6 | 7 | #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] 8 | pub enum AdsrState { 9 | Disabled, 10 | Attack, 11 | Decay, 12 | Sustain, 13 | Release, 14 | } 15 | 16 | impl Default for AdsrState { 17 | fn default() -> AdsrState { 18 | AdsrState::Disabled 19 | } 20 | } 21 | 22 | #[derive(PartialEq)] 23 | pub enum AdsrMode { 24 | Linear, 25 | Exponential, 26 | } 27 | 28 | #[derive(PartialEq)] 29 | pub enum AdsrDirection { 30 | Increase, 31 | Decrease, 32 | } 33 | 34 | /* 35 | * TODO: Perhaps encode this information when writing the u32 config 36 | * register this would make it much neater, whilst also probably being faster 37 | */ 38 | struct AdsrConfig { 39 | pub mode: AdsrMode, 40 | pub direction: AdsrDirection, 41 | pub shift: isize, 42 | pub step: isize, 43 | pub target: isize, 44 | pub next: AdsrState, 45 | } 46 | 47 | impl AdsrConfig { 48 | fn attack_mode(config: u32) -> AdsrMode { 49 | match (config & 0x8000) != 0 { 50 | false => AdsrMode::Linear, 51 | true => AdsrMode::Exponential, 52 | } 53 | } 54 | 55 | fn attack_shift(config: u32) -> isize { 56 | ((config & 0x7c00) >> 10) as isize 57 | } 58 | 59 | fn attack_step(config: u32) -> isize { 60 | let step = ((config & 0x300) >> 8) as isize; 61 | 62 | 7 - step 63 | } 64 | 65 | fn decay_shift(config: u32) -> isize { 66 | ((config & 0xf0) >> 4) as isize 67 | } 68 | 69 | fn sustain_mode(config: u32) -> AdsrMode { 70 | match (config & 0x8000_0000) != 0 { 71 | false => AdsrMode::Linear, 72 | true => AdsrMode::Exponential, 73 | } 74 | } 75 | 76 | fn sustain_direction(config: u32) -> AdsrDirection { 77 | match (config & 0x4000_0000) != 0 { 78 | false => AdsrDirection::Increase, 79 | true => AdsrDirection::Decrease, 80 | } 81 | } 82 | 83 | fn sustain_shift(config: u32) -> isize { 84 | ((config & 0x1f00_0000) >> 24) as isize 85 | } 86 | 87 | fn sustain_step(config: u32) -> isize { 88 | let step = ((config & 0xc0_0000) >> 22) as isize; 89 | 90 | match AdsrConfig::sustain_direction(config) { 91 | AdsrDirection::Increase => 7 - step, 92 | AdsrDirection::Decrease => -8 + step, 93 | } 94 | } 95 | 96 | fn sustain_level(config: u32) -> isize { 97 | let level = (config & 0xf) as isize; 98 | 99 | (level + 1) * 0x800 100 | } 101 | 102 | fn release_mode(config: u32) -> AdsrMode { 103 | match (config & 0x20_0000) != 0 { 104 | false => AdsrMode::Linear, 105 | true => AdsrMode::Exponential, 106 | } 107 | } 108 | 109 | fn release_shift(config: u32) -> isize { 110 | ((config & 0x1f_0000) >> 16) as isize 111 | } 112 | 113 | pub fn from(state: AdsrState, config: u32) -> AdsrConfig { 114 | let mode = match state { 115 | AdsrState::Disabled => AdsrMode::Linear, 116 | AdsrState::Attack => AdsrConfig::attack_mode(config), 117 | AdsrState::Decay => AdsrMode::Exponential, 118 | AdsrState::Sustain => AdsrConfig::sustain_mode(config), 119 | AdsrState::Release => AdsrConfig::release_mode(config), 120 | }; 121 | 122 | let direction = match state { 123 | AdsrState::Disabled => AdsrDirection::Increase, 124 | AdsrState::Attack => AdsrDirection::Increase, 125 | AdsrState::Decay => AdsrDirection::Decrease, 126 | AdsrState::Sustain => AdsrConfig::sustain_direction(config), 127 | AdsrState::Release => AdsrDirection::Decrease, 128 | }; 129 | 130 | let shift = match state { 131 | AdsrState::Disabled => 0, 132 | AdsrState::Attack => AdsrConfig::attack_shift(config), 133 | AdsrState::Decay => AdsrConfig::decay_shift(config), 134 | AdsrState::Sustain => AdsrConfig::sustain_shift(config), 135 | AdsrState::Release => AdsrConfig::release_shift(config), 136 | }; 137 | 138 | let step = match state { 139 | AdsrState::Disabled => 0, 140 | AdsrState::Attack => AdsrConfig::attack_step(config), 141 | AdsrState::Decay => -8, 142 | AdsrState::Sustain => AdsrConfig::sustain_step(config), 143 | AdsrState::Release => -8, 144 | }; 145 | 146 | let target = match state { 147 | AdsrState::Disabled => -1, 148 | AdsrState::Attack => 0x7fff, 149 | AdsrState::Decay => AdsrConfig::sustain_level(config), 150 | AdsrState::Sustain => -1, 151 | AdsrState::Release => 0, 152 | }; 153 | 154 | let next = match state { 155 | AdsrState::Disabled => AdsrState::Disabled, 156 | AdsrState::Attack => AdsrState::Decay, 157 | AdsrState::Decay => AdsrState::Sustain, 158 | AdsrState::Sustain => AdsrState::Sustain, 159 | AdsrState::Release => AdsrState::Disabled, 160 | }; 161 | 162 | AdsrConfig { 163 | mode: mode, 164 | direction: direction, 165 | shift: shift, 166 | step: step, 167 | target: target, 168 | next: next, 169 | } 170 | } 171 | } 172 | 173 | #[derive(Clone, Copy, Default, Deserialize, Serialize)] 174 | pub struct Adsr { 175 | pub cycles: isize, 176 | 177 | pub state: AdsrState, 178 | pub config: u32, 179 | pub volume: i16, 180 | } 181 | 182 | impl Adsr { 183 | pub fn update(&mut self) { 184 | if self.cycles > 0 { 185 | self.cycles -= 1; 186 | } 187 | 188 | let c = AdsrConfig::from(self.state, self.config); 189 | 190 | let mut cycles = 1 << cmp::max(0, c.shift - 11); 191 | let mut step = c.step << cmp::max(0, 11 - c.shift); 192 | 193 | if c.mode == AdsrMode::Exponential { 194 | if c.direction == AdsrDirection::Increase { 195 | if self.volume > 0x6000 { 196 | cycles *= 4; 197 | } 198 | } else { 199 | step = (step * self.volume as isize) >> 15; 200 | } 201 | } 202 | 203 | if self.cycles <= 0 { 204 | self.cycles += cycles; 205 | self.volume = clip(self.volume as isize + step, 0, 0x7fff) as i16; 206 | 207 | if c.target < 0 { 208 | return; 209 | } 210 | 211 | if (c.direction == AdsrDirection::Increase && self.volume as isize >= c.target) 212 | || (c.direction == AdsrDirection::Decrease && self.volume as isize <= c.target) 213 | { 214 | self.state = c.next; 215 | self.cycles = 0; 216 | } 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/psx/spu/gauss.rs: -------------------------------------------------------------------------------- 1 | pub const GAUSS_TABLE: [i32; 0x200] = [ 2 | -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, 3 | -0x001, -0x001, -0x001, -0x001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 4 | 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, 0x0005, 5 | 0x0005, 0x0006, 0x0007, 0x0007, 0x0008, 0x0009, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 6 | 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0015, 0x0016, 0x0018, 0x0019, 0x001B, 0x001C, 0x001E, 7 | 0x0020, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002C, 0x002E, 0x0030, 0x0033, 0x0035, 0x0038, 8 | 0x003A, 0x003D, 0x0040, 0x0043, 0x0046, 0x0049, 0x004D, 0x0050, 0x0054, 0x0057, 0x005B, 0x005F, 9 | 0x0063, 0x0067, 0x006B, 0x006F, 0x0074, 0x0078, 0x007D, 0x0082, 0x0087, 0x008C, 0x0091, 0x0096, 10 | 0x009C, 0x00A1, 0x00A7, 0x00AD, 0x00B3, 0x00BA, 0x00C0, 0x00C7, 0x00CD, 0x00D4, 0x00DB, 0x00E3, 11 | 0x00EA, 0x00F2, 0x00FA, 0x0101, 0x010A, 0x0112, 0x011B, 0x0123, 0x012C, 0x0135, 0x013F, 0x0148, 12 | 0x0152, 0x015C, 0x0166, 0x0171, 0x017B, 0x0186, 0x0191, 0x019C, 0x01A8, 0x01B4, 0x01C0, 0x01CC, 13 | 0x01D9, 0x01E5, 0x01F2, 0x0200, 0x020D, 0x021B, 0x0229, 0x0237, 0x0246, 0x0255, 0x0264, 0x0273, 14 | 0x0283, 0x0293, 0x02A3, 0x02B4, 0x02C4, 0x02D6, 0x02E7, 0x02F9, 0x030B, 0x031D, 0x0330, 0x0343, 15 | 0x0356, 0x036A, 0x037E, 0x0392, 0x03A7, 0x03BC, 0x03D1, 0x03E7, 0x03FC, 0x0413, 0x042A, 0x0441, 16 | 0x0458, 0x0470, 0x0488, 0x04A0, 0x04B9, 0x04D2, 0x04EC, 0x0506, 0x0520, 0x053B, 0x0556, 0x0572, 17 | 0x058E, 0x05AA, 0x05C7, 0x05E4, 0x0601, 0x061F, 0x063E, 0x065C, 0x067C, 0x069B, 0x06BB, 0x06DC, 18 | 0x06FD, 0x071E, 0x0740, 0x0762, 0x0784, 0x07A7, 0x07CB, 0x07EF, 0x0813, 0x0838, 0x085D, 0x0883, 19 | 0x08A9, 0x08D0, 0x08F7, 0x091E, 0x0946, 0x096F, 0x0998, 0x09C1, 0x09EB, 0x0A16, 0x0A40, 0x0A6C, 20 | 0x0A98, 0x0AC4, 0x0AF1, 0x0B1E, 0x0B4C, 0x0B7A, 0x0BA9, 0x0BD8, 0x0C07, 0x0C38, 0x0C68, 0x0C99, 21 | 0x0CCB, 0x0CFD, 0x0D30, 0x0D63, 0x0D97, 0x0DCB, 0x0E00, 0x0E35, 0x0E6B, 0x0EA1, 0x0ED7, 0x0F0F, 22 | 0x0F46, 0x0F7F, 0x0FB7, 0x0FF1, 0x102A, 0x1065, 0x109F, 0x10DB, 0x1116, 0x1153, 0x118F, 0x11CD, 23 | 0x120B, 0x1249, 0x1288, 0x12C7, 0x1307, 0x1347, 0x1388, 0x13C9, 0x140B, 0x144D, 0x1490, 0x14D4, 24 | 0x1517, 0x155C, 0x15A0, 0x15E6, 0x162C, 0x1672, 0x16B9, 0x1700, 0x1747, 0x1790, 0x17D8, 0x1821, 25 | 0x186B, 0x18B5, 0x1900, 0x194B, 0x1996, 0x19E2, 0x1A2E, 0x1A7B, 0x1AC8, 0x1B16, 0x1B64, 0x1BB3, 26 | 0x1C02, 0x1C51, 0x1CA1, 0x1CF1, 0x1D42, 0x1D93, 0x1DE5, 0x1E37, 0x1E89, 0x1EDC, 0x1F2F, 0x1F82, 27 | 0x1FD6, 0x202A, 0x207F, 0x20D4, 0x2129, 0x217F, 0x21D5, 0x222C, 0x2282, 0x22DA, 0x2331, 0x2389, 28 | 0x23E1, 0x2439, 0x2492, 0x24EB, 0x2545, 0x259E, 0x25F8, 0x2653, 0x26AD, 0x2708, 0x2763, 0x27BE, 29 | 0x281A, 0x2876, 0x28D2, 0x292E, 0x298B, 0x29E7, 0x2A44, 0x2AA1, 0x2AFF, 0x2B5C, 0x2BBA, 0x2C18, 30 | 0x2C76, 0x2CD4, 0x2D33, 0x2D91, 0x2DF0, 0x2E4F, 0x2EAE, 0x2F0D, 0x2F6C, 0x2FCC, 0x302B, 0x308B, 31 | 0x30EA, 0x314A, 0x31AA, 0x3209, 0x3269, 0x32C9, 0x3329, 0x3389, 0x33E9, 0x3449, 0x34A9, 0x3509, 32 | 0x3569, 0x35C9, 0x3629, 0x3689, 0x36E8, 0x3748, 0x37A8, 0x3807, 0x3867, 0x38C6, 0x3926, 0x3985, 33 | 0x39E4, 0x3A43, 0x3AA2, 0x3B00, 0x3B5F, 0x3BBD, 0x3C1B, 0x3C79, 0x3CD7, 0x3D35, 0x3D92, 0x3DEF, 34 | 0x3E4C, 0x3EA9, 0x3F05, 0x3F62, 0x3FBD, 0x4019, 0x4074, 0x40D0, 0x412A, 0x4185, 0x41DF, 0x4239, 35 | 0x4292, 0x42EB, 0x4344, 0x439C, 0x43F4, 0x444C, 0x44A3, 0x44FA, 0x4550, 0x45A6, 0x45FC, 0x4651, 36 | 0x46A6, 0x46FA, 0x474E, 0x47A1, 0x47F4, 0x4846, 0x4898, 0x48E9, 0x493A, 0x498A, 0x49D9, 0x4A29, 37 | 0x4A77, 0x4AC5, 0x4B13, 0x4B5F, 0x4BAC, 0x4BF7, 0x4C42, 0x4C8D, 0x4CD7, 0x4D20, 0x4D68, 0x4DB0, 38 | 0x4DF7, 0x4E3E, 0x4E84, 0x4EC9, 0x4F0E, 0x4F52, 0x4F95, 0x4FD7, 0x5019, 0x505A, 0x509A, 0x50DA, 39 | 0x5118, 0x5156, 0x5194, 0x51D0, 0x520C, 0x5247, 0x5281, 0x52BA, 0x52F3, 0x532A, 0x5361, 0x5397, 40 | 0x53CC, 0x5401, 0x5434, 0x5467, 0x5499, 0x54CA, 0x54FA, 0x5529, 0x5558, 0x5585, 0x55B2, 0x55DE, 41 | 0x5609, 0x5632, 0x565B, 0x5684, 0x56AB, 0x56D1, 0x56F6, 0x571B, 0x573E, 0x5761, 0x5782, 0x57A3, 42 | 0x57C3, 0x57E2, 0x57FF, 0x581C, 0x5838, 0x5853, 0x586D, 0x5886, 0x589E, 0x58B5, 0x58CB, 0x58E0, 43 | 0x58F4, 0x5907, 0x5919, 0x592A, 0x593A, 0x5949, 0x5958, 0x5965, 0x5971, 0x597C, 0x5986, 0x598F, 44 | 0x5997, 0x599E, 0x59A4, 0x59A9, 0x59AD, 0x59B0, 0x59B2, 0x59B3, 45 | ]; 46 | -------------------------------------------------------------------------------- /src/psx/spu/mod.rs: -------------------------------------------------------------------------------- 1 | mod adsr; 2 | mod gauss; 3 | mod reverb; 4 | mod voice; 5 | mod volume; 6 | 7 | use std::collections::VecDeque; 8 | 9 | use serde::{Deserialize, Serialize}; 10 | 11 | use crate::util::{clip, f32_to_i16, i16_to_f32}; 12 | 13 | use super::intc::{Intc, Interrupt}; 14 | 15 | use self::reverb::Reverb; 16 | use self::voice::Voice; 17 | use self::volume::Volume; 18 | 19 | const SPU_BUFFER_SIZE: usize = 32768; 20 | 21 | const SPU_FIFO_SIZE: usize = 32; 22 | 23 | const SPU_RAM_SIZE: usize = 0x80000; 24 | const SPU_WORD_SIZE: usize = 2; 25 | 26 | const SPU_NR_VOICES: usize = 24; 27 | 28 | const NOISE_WAVE_TABLE: [isize; 64] = [ 29 | 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 30 | 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 31 | ]; 32 | 33 | const NOISE_FREQ_TABLE: [isize; 5] = [0, 84, 140, 180, 210]; 34 | 35 | #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] 36 | enum SpuTransferMode { 37 | Stop, 38 | ManualWrite, 39 | DmaWrite, 40 | DmaRead, 41 | } 42 | 43 | impl Default for SpuTransferMode { 44 | fn default() -> SpuTransferMode { 45 | SpuTransferMode::Stop 46 | } 47 | } 48 | 49 | impl From for SpuTransferMode { 50 | fn from(value: u16) -> SpuTransferMode { 51 | use self::SpuTransferMode::*; 52 | 53 | match value & 0x3 { 54 | 0 => Stop, 55 | 1 => ManualWrite, 56 | 2 => DmaWrite, 57 | 3 => DmaRead, 58 | _ => unreachable!(), 59 | } 60 | } 61 | } 62 | 63 | #[derive(Default, Deserialize, Serialize)] 64 | struct SpuControl { 65 | enable: bool, 66 | mute: bool, 67 | noise_clock: u16, 68 | reverb_enable: bool, 69 | irq9_enable: bool, 70 | transfer_mode: SpuTransferMode, 71 | external_reverb: bool, 72 | cd_reverb: bool, 73 | external_enable: bool, 74 | cd_enable: bool, 75 | } 76 | 77 | impl SpuControl { 78 | pub fn read(&self) -> u16 { 79 | let mut value = 0; 80 | 81 | value |= (self.enable as u16) << 15; 82 | value |= (self.mute as u16) << 14; 83 | value |= (self.noise_clock & 0x3f) << 8; 84 | value |= (self.reverb_enable as u16) << 7; 85 | value |= (self.irq9_enable as u16) << 6; 86 | value |= (self.transfer_mode as u16) << 4; 87 | value |= (self.external_reverb as u16) << 3; 88 | value |= (self.cd_reverb as u16) << 2; 89 | value |= (self.external_enable as u16) << 1; 90 | value |= self.cd_enable as u16; 91 | 92 | value 93 | } 94 | 95 | pub fn write(&mut self, value: u16) -> bool { 96 | self.enable = (value & 0x8000) != 0; 97 | self.mute = (value & 0x4000) != 0; 98 | self.noise_clock = (value & 0x3f00) >> 8; 99 | self.reverb_enable = (value & 0x80) != 0; 100 | self.irq9_enable = (value & 0x40) != 0; 101 | self.transfer_mode = SpuTransferMode::from((value & 0x30) >> 4); 102 | self.external_reverb = (value & 0x8) != 0; 103 | self.cd_reverb = (value & 0x4) != 0; 104 | self.external_enable = (value & 0x2) != 0; 105 | self.cd_enable = (value & 0x1) != 0; 106 | 107 | !self.irq9_enable 108 | } 109 | } 110 | 111 | #[derive(Default, Deserialize, Serialize)] 112 | struct SpuDataTransfer { 113 | address: u32, 114 | current: u32, 115 | 116 | fifo: VecDeque, 117 | 118 | control: u16, 119 | } 120 | 121 | #[derive(Deserialize, Serialize)] 122 | pub struct SpuRam { 123 | data: Box<[u16]>, 124 | 125 | irq_address: u32, 126 | irq: bool, 127 | } 128 | 129 | impl SpuRam { 130 | pub fn new(size: usize) -> SpuRam { 131 | SpuRam { 132 | data: vec![0; size].into_boxed_slice(), 133 | 134 | irq_address: 0, 135 | irq: false, 136 | } 137 | } 138 | 139 | pub fn irq(&mut self) -> bool { 140 | let irq = self.irq; 141 | self.irq = false; 142 | irq 143 | } 144 | 145 | pub fn memory_read16(&mut self, address: u32) -> u16 { 146 | let index = (address & 0x7fffe) as usize; 147 | 148 | if address >= 0x800 && address < 0x1000 { 149 | println!( 150 | "[SPU] [WARN] Read from voice1/3 buffer at 0x{:08x}", 151 | address 152 | ); 153 | } 154 | 155 | if (address & 0x7fffe) == self.irq_address { 156 | self.irq = true; 157 | } 158 | 159 | self.data[index / 2] 160 | } 161 | 162 | pub fn memory_write16(&mut self, address: u32, value: u16) { 163 | let index = (address & 0x7fffe) as usize; 164 | 165 | if (address & 0x7ffff) == self.irq_address { 166 | self.irq = true; 167 | } 168 | 169 | if ((address + 1) & 0x7ffff) == self.irq_address { 170 | self.irq = true; 171 | } 172 | 173 | self.data[index / 2] = value; 174 | } 175 | } 176 | 177 | #[derive(Deserialize, Serialize)] 178 | pub struct Spu { 179 | output_buffer: Vec, 180 | 181 | cd_left_buffer: VecDeque, 182 | cd_right_buffer: VecDeque, 183 | 184 | capture_index: u32, 185 | 186 | sound_ram: SpuRam, 187 | 188 | voice: [Voice; SPU_NR_VOICES], 189 | 190 | main_volume: Volume, 191 | reverb_volume: Volume, 192 | 193 | key_on: u32, 194 | key_off: u32, 195 | endx: u32, 196 | echo_on: u32, 197 | 198 | modulate_on: u32, 199 | 200 | noise_on: u32, 201 | noise_timer: isize, 202 | noise_level: i16, 203 | 204 | control: SpuControl, 205 | 206 | reverb: Reverb, 207 | 208 | data_transfer: SpuDataTransfer, 209 | 210 | irq_status: bool, 211 | 212 | writing_to_capture_buffer_half: bool, 213 | data_transfer_busy: bool, 214 | data_transfer_dma_read: bool, 215 | data_transfer_dma_write: bool, 216 | 217 | cd_volume: Volume, 218 | extern_volume: Volume, 219 | current_volume: Volume, 220 | } 221 | 222 | impl Spu { 223 | pub fn new() -> Spu { 224 | Spu { 225 | output_buffer: Vec::with_capacity(SPU_BUFFER_SIZE), 226 | 227 | cd_left_buffer: VecDeque::new(), 228 | cd_right_buffer: VecDeque::new(), 229 | 230 | capture_index: 0, 231 | 232 | sound_ram: SpuRam::new(SPU_RAM_SIZE / SPU_WORD_SIZE), 233 | 234 | voice: [Voice::new(); SPU_NR_VOICES], 235 | 236 | main_volume: Volume::default(), 237 | reverb_volume: Volume::default(), 238 | 239 | key_on: 0, 240 | key_off: 0, 241 | endx: 0, 242 | echo_on: 0, 243 | 244 | modulate_on: 0, 245 | 246 | noise_on: 0, 247 | noise_timer: 0, 248 | noise_level: 1, 249 | 250 | control: SpuControl::default(), 251 | 252 | reverb: Reverb::default(), 253 | 254 | data_transfer: SpuDataTransfer::default(), 255 | 256 | irq_status: false, 257 | 258 | writing_to_capture_buffer_half: false, 259 | data_transfer_busy: false, 260 | data_transfer_dma_read: false, 261 | data_transfer_dma_write: false, 262 | 263 | cd_volume: Volume::default(), 264 | extern_volume: Volume::default(), 265 | current_volume: Volume::default(), 266 | } 267 | } 268 | 269 | fn update_key_on(&mut self) { 270 | for i in 0..24 { 271 | if (self.key_on & (1 << i)) != 0 { 272 | self.voice[i].key_on(); 273 | } 274 | } 275 | 276 | self.key_on = 0; 277 | } 278 | 279 | fn update_key_off(&mut self) { 280 | for i in 0..24 { 281 | if (self.key_off & (1 << i)) != 0 { 282 | self.voice[i].key_off(); 283 | } 284 | } 285 | 286 | self.key_off = 0; 287 | } 288 | 289 | fn update_echo(&mut self) { 290 | for i in 0..24 { 291 | if (self.echo_on & (1 << i)) != 0 { 292 | self.voice[i].echo_on(); 293 | } 294 | } 295 | } 296 | 297 | fn update_endx(&mut self) { 298 | self.endx = 0; 299 | 300 | for i in 0..24 { 301 | if self.voice[i].endx() { 302 | self.endx |= 1 << i; 303 | } 304 | } 305 | } 306 | 307 | fn update_noise(&mut self) { 308 | for i in 0..24 { 309 | self.voice[i].set_noise((self.noise_on & (1 << i)) != 0); 310 | } 311 | 312 | let noise_clock = (self.control.noise_clock & 0x3) as usize; 313 | 314 | let mut level = 0x8000 >> (self.control.noise_clock >> 2); 315 | level <<= 16; 316 | 317 | self.noise_timer += 0x10000; 318 | self.noise_timer += NOISE_FREQ_TABLE[noise_clock]; 319 | 320 | if (self.noise_timer & 0xffff) >= NOISE_FREQ_TABLE[4] { 321 | self.noise_timer += 0x10000; 322 | self.noise_timer -= NOISE_FREQ_TABLE[noise_clock]; 323 | } 324 | 325 | if self.noise_timer >= level { 326 | self.noise_timer %= level; 327 | 328 | let bit = NOISE_WAVE_TABLE[((self.noise_level >> 10) & 0x3f) as usize] as i16; 329 | self.noise_level = (self.noise_level << 1) | bit; 330 | } 331 | } 332 | 333 | pub fn tick(&mut self, intc: &mut Intc) { 334 | let mut left = 0.0; 335 | let mut right = 0.0; 336 | 337 | let mut cd_left = 0.0; 338 | let mut cd_right = 0.0; 339 | 340 | let mut reverb_in_left = 0.0; 341 | let mut reverb_in_right = 0.0; 342 | 343 | if !self.cd_left_buffer.is_empty() { 344 | cd_left = i16_to_f32(self.cd_left_buffer.pop_front().unwrap()); 345 | } 346 | 347 | if !self.cd_right_buffer.is_empty() { 348 | cd_right = i16_to_f32(self.cd_right_buffer.pop_front().unwrap()); 349 | } 350 | 351 | /* TODO: Maybe do this upon writing only? */ 352 | self.update_key_on(); 353 | self.update_key_off(); 354 | self.update_endx(); 355 | self.update_echo(); 356 | self.update_noise(); 357 | 358 | let mut modulator = 0; 359 | let noise_level = i16_to_f32(self.noise_level); 360 | 361 | for i in 0..self.voice.len() { 362 | let voice = &mut self.voice[i]; 363 | let modulate = i != 0 && (self.modulate_on & (1 << i)) != 0; 364 | let noise = (self.noise_on & (1 << i)) != 0; 365 | 366 | if voice.disabled() { 367 | continue; 368 | } 369 | 370 | let (sample_left, sample_right) = voice.get_samples(noise, noise_level); 371 | 372 | left += sample_left; 373 | right += sample_right; 374 | 375 | if voice.reverb_enabled() { 376 | reverb_in_left += sample_left; 377 | reverb_in_right += sample_right; 378 | } 379 | 380 | voice.update(&mut self.sound_ram, modulate, modulator); 381 | 382 | modulator = voice.modulator; 383 | } 384 | 385 | left *= self.main_volume.l(); 386 | right *= self.main_volume.r(); 387 | 388 | if self.control.reverb_enable { 389 | left += self.reverb.output_l() * self.reverb_volume.l(); 390 | right += self.reverb.output_r() * self.reverb_volume.r(); 391 | } 392 | 393 | if self.control.cd_enable { 394 | left += cd_left * self.cd_volume.l(); 395 | right += cd_right * self.cd_volume.r(); 396 | } 397 | 398 | if self.control.cd_reverb { 399 | reverb_in_left += cd_left * self.cd_volume.l(); 400 | reverb_in_right += cd_right * self.cd_volume.r(); 401 | } 402 | 403 | left = clip(left, -1.0, 1.0); 404 | right = clip(right, -1.0, 1.0); 405 | 406 | reverb_in_left = clip(reverb_in_left, -1.0, 1.0); 407 | reverb_in_right = clip(reverb_in_right, -1.0, 1.0); 408 | 409 | if self.control.reverb_enable { 410 | self.reverb 411 | .calculate(&mut self.sound_ram, [reverb_in_left, reverb_in_right]); 412 | } 413 | 414 | self.sound_ram 415 | .memory_write16(0x000 + self.capture_index, f32_to_i16(cd_left) as u16); 416 | self.sound_ram 417 | .memory_write16(0x400 + self.capture_index, f32_to_i16(cd_right) as u16); 418 | 419 | /* Fake writes to capture buffer */ 420 | self.sound_ram.memory_write16(0x800 + self.capture_index, 0); /* Voice 1 */ 421 | self.sound_ram.memory_write16(0xc00 + self.capture_index, 0); /* Voice 3 */ 422 | 423 | self.capture_index = (self.capture_index + 2) & 0x3ff; 424 | self.writing_to_capture_buffer_half = self.capture_index >= 0x200; 425 | 426 | if self.sound_ram.irq() && self.control.irq9_enable { 427 | intc.assert_irq(Interrupt::Spu); 428 | self.irq_status = true; 429 | } 430 | 431 | /* TODO: Maybe ringbuffer? */ 432 | self.output_buffer.push(f32_to_i16(left)); 433 | self.output_buffer.push(f32_to_i16(right)); 434 | } 435 | 436 | pub fn drain_samples(&mut self) -> Vec { 437 | self.output_buffer.drain(..).collect() 438 | } 439 | 440 | fn read_status(&self) -> u16 { 441 | let mut value = 0; 442 | let control = self.control.read(); 443 | 444 | value |= (self.writing_to_capture_buffer_half as u16) << 11; 445 | value |= (self.data_transfer_busy as u16) << 10; 446 | value |= (self.data_transfer_dma_read as u16) << 9; 447 | value |= (self.data_transfer_dma_write as u16) << 8; 448 | value |= (control & 0x20) << 2; 449 | value |= (self.irq_status as u16) << 6; 450 | value |= control & 0x3f; 451 | 452 | value 453 | } 454 | 455 | fn push_fifo(&mut self, value: u16) { 456 | if self.data_transfer.fifo.len() < SPU_FIFO_SIZE { 457 | self.data_transfer.fifo.push_back(value); 458 | } 459 | } 460 | 461 | pub fn read16(&mut self, address: u32) -> u16 { 462 | match address { 463 | 0x1f801c00..=0x1f801d7f => { 464 | let (voice, offset) = Voice::from_address(address); 465 | self.voice[voice].read16(offset) 466 | } 467 | 0x1f801d80 => self.main_volume.left as u16, 468 | 0x1f801d82 => self.main_volume.right as u16, 469 | 0x1f801d84 => self.reverb_volume.left as u16, 470 | 0x1f801d86 => self.reverb_volume.right as u16, 471 | 0x1f801d88 => { 472 | println!("[SPU] [WARN] Read from KON register"); 473 | self.key_on as u16 474 | } 475 | 0x1f801d8a => { 476 | println!("[SPU] [WARN] Read from KON register"); 477 | (self.key_on >> 16) as u16 478 | } 479 | 0x1f801d8c => { 480 | println!("[SPU] [WARN] Read from KOFF register"); 481 | self.key_off as u16 482 | } 483 | 0x1f801d8e => { 484 | println!("[SPU] [WARN] Read from KOFF register"); 485 | (self.key_off >> 16) as u16 486 | } 487 | 0x1f801d90 => self.modulate_on as u16, 488 | 0x1f801d92 => (self.modulate_on >> 16) as u16, 489 | 0x1f801d94 => self.noise_on as u16, 490 | 0x1f801d96 => (self.noise_on >> 16) as u16, 491 | 0x1f801d98 => self.echo_on as u16, 492 | 0x1f801d9a => (self.echo_on >> 16) as u16, 493 | 0x1f801d9c => self.endx as u16, 494 | 0x1f801d9e => (self.endx >> 16) as u16, 495 | 0x1f801da2 => self.reverb.get_base(), 496 | 0x1f801da6 => (self.data_transfer.address / 8) as u16, 497 | 0x1f801da8 => { 498 | println!("[SPU] [WARN] Read from data transfer FIFO"); 499 | 0 500 | } 501 | 0x1f801daa => self.control.read(), 502 | 0x1f801dac => self.data_transfer.control, 503 | 0x1f801dae => self.read_status(), 504 | 0x1f801db0 => self.cd_volume.left as u16, 505 | 0x1f801db2 => self.cd_volume.right as u16, 506 | 0x1f801db4 => self.extern_volume.left as u16, 507 | 0x1f801db6 => self.extern_volume.right as u16, 508 | 0x1f801db8 => self.current_volume.left as u16, 509 | 0x1f801dba => self.current_volume.right as u16, 510 | 0x1f801dc0..=0x1f801dff => self.reverb.read16(address), 511 | 0x1f801e00..=0x1f801fff => 0xffff, 512 | _ => panic!( 513 | "[SPU] [ERROR] Read from unimplemented register: 0x{:08x}", 514 | address 515 | ), 516 | } 517 | } 518 | 519 | pub fn read32(&mut self, address: u32) -> u32 { 520 | ((self.read16(address + 1) as u32) << 16) | self.read16(address) as u32 521 | } 522 | 523 | pub fn write16(&mut self, address: u32, value: u16) { 524 | match address { 525 | 0x1f801c00..=0x1f801d7f => { 526 | let (voice, offset) = Voice::from_address(address); 527 | self.voice[voice].write16(offset, value) 528 | } 529 | 0x1f801d80 => self.main_volume.left = value as i16, 530 | 0x1f801d82 => self.main_volume.right = value as i16, 531 | 0x1f801d84 => self.reverb_volume.left = value as i16, 532 | 0x1f801d86 => self.reverb_volume.right = value as i16, 533 | 0x1f801d88 => { 534 | self.key_on &= 0xffff0000; 535 | self.key_on |= value as u32; 536 | } 537 | 0x1f801d8a => { 538 | self.key_on &= 0xffff; 539 | self.key_on |= (value as u32) << 16; 540 | } 541 | 0x1f801d8c => { 542 | self.key_off &= 0xffff0000; 543 | self.key_off |= value as u32; 544 | } 545 | 0x1f801d8e => { 546 | self.key_off &= 0xffff; 547 | self.key_off |= (value as u32) << 16; 548 | } 549 | 0x1f801d90 => { 550 | self.modulate_on &= 0xffff0000; 551 | self.modulate_on |= value as u32; 552 | } 553 | 0x1f801d92 => { 554 | self.modulate_on &= 0xffff; 555 | self.modulate_on |= (value as u32) << 16; 556 | } 557 | 0x1f801d94 => { 558 | self.noise_on &= 0xffff0000; 559 | self.noise_on |= value as u32; 560 | } 561 | 0x1f801d96 => { 562 | self.noise_on &= 0xffff; 563 | self.noise_on |= (value as u32) << 16; 564 | } 565 | 0x1f801d98 => { 566 | self.echo_on &= 0xffff0000; 567 | self.echo_on |= value as u32; 568 | } 569 | 0x1f801d9a => { 570 | self.echo_on &= 0xffff; 571 | self.echo_on |= (value as u32) << 16; 572 | } 573 | 0x1f801d9c => println!("[SPU] [WARN] Write to ENDX register"), 574 | 0x1f801d9e => println!("[SPU] [WARN] Write to ENDX register"), 575 | 0x1f801da2 => self.reverb.set_base(value), 576 | 0x1f801da4 => { 577 | self.sound_ram.irq_address = (value as u32) * 8; 578 | //println!("[SPU] IRQ Address: 0x{:08x}", self.sound_ram.irq_address); 579 | } 580 | 0x1f801da6 => { 581 | self.data_transfer.address = (value as u32) * 8; 582 | self.data_transfer.current = (value as u32) * 8; 583 | } 584 | 0x1f801da8 => self.push_fifo(value), 585 | 0x1f801daa => { 586 | if self.control.write(value as u16) { 587 | self.irq_status = false; 588 | } 589 | 590 | if self.control.transfer_mode == SpuTransferMode::ManualWrite { 591 | while !self.data_transfer.fifo.is_empty() { 592 | let data = self.data_transfer.fifo.pop_front().unwrap(); 593 | let address = self.data_transfer.current; 594 | 595 | self.sound_ram.memory_write16(address, data); 596 | 597 | self.data_transfer.current += 2; 598 | self.data_transfer.current &= 0x7ffff; 599 | } 600 | } 601 | } 602 | 0x1f801dac => self.data_transfer.control = value, 603 | 0x1f801dae => println!("[SPU] [WARN] Write to SPUSTAT"), 604 | 0x1f801db0 => self.cd_volume.left = value as i16, 605 | 0x1f801db2 => self.cd_volume.right = value as i16, 606 | 0x1f801db4 => self.extern_volume.left = value as i16, 607 | 0x1f801db6 => self.extern_volume.right = value as i16, 608 | 0x1f801db8 => self.current_volume.left = value as i16, 609 | 0x1f801dba => self.current_volume.right = value as i16, 610 | 0x1f801dc0..=0x1f801dff => self.reverb.write16(address, value), 611 | _ => panic!( 612 | "[SPU] [ERROR] Write to unimplemented register: 0x{:08x}", 613 | address 614 | ), 615 | }; 616 | } 617 | 618 | pub fn cd_push(&mut self, left: i16, right: i16) { 619 | self.cd_left_buffer.push_back(left); 620 | self.cd_right_buffer.push_back(right); 621 | } 622 | 623 | pub fn cd_push_left(&mut self, sample: i16) { 624 | self.cd_left_buffer.push_back(sample); 625 | } 626 | 627 | pub fn cd_push_right(&mut self, sample: i16) { 628 | self.cd_right_buffer.push_back(sample); 629 | } 630 | 631 | pub fn dma_read(&mut self) -> u32 { 632 | let address = self.data_transfer.current; 633 | 634 | let lo = self.sound_ram.memory_read16(address) as u32; 635 | let hi = self.sound_ram.memory_read16(address + 2) as u32; 636 | 637 | self.data_transfer.current += 4; 638 | self.data_transfer.current &= 0x7ffff; 639 | 640 | (hi << 16) | lo 641 | } 642 | 643 | pub fn dma_write(&mut self, value: u32) { 644 | let address = self.data_transfer.current; 645 | 646 | self.sound_ram.memory_write16(address, value as u16); 647 | self.sound_ram 648 | .memory_write16(address + 2, (value >> 16) as u16); 649 | 650 | self.data_transfer.current += 4; 651 | self.data_transfer.current &= 0x7ffff; 652 | } 653 | } 654 | -------------------------------------------------------------------------------- /src/psx/spu/reverb.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::util::{clip, f32_to_i16, i16_to_f32}; 6 | 7 | use super::SpuRam; 8 | 9 | #[derive(Debug, Default, Deserialize, Serialize)] 10 | pub struct Reverb { 11 | counter: usize, 12 | output: [f32; 2], 13 | 14 | buffer_address: u32, 15 | 16 | mbase: u32, 17 | 18 | dapf1: u32, 19 | dapf2: u32, 20 | 21 | viir: i16, 22 | vcomb1: i16, 23 | vcomb2: i16, 24 | vcomb3: i16, 25 | vcomb4: i16, 26 | vwall: i16, 27 | vapf1: i16, 28 | vapf2: i16, 29 | 30 | msame: [u32; 2], 31 | mcomb1: [u32; 2], 32 | mcomb2: [u32; 2], 33 | 34 | dsame: [u32; 2], 35 | 36 | mdiff: [u32; 2], 37 | mcomb3: [u32; 2], 38 | mcomb4: [u32; 2], 39 | 40 | ddiff: [u32; 2], 41 | 42 | mapf1: [u32; 2], 43 | mapf2: [u32; 2], 44 | 45 | vin: [i16; 2], 46 | } 47 | 48 | impl Reverb { 49 | pub fn calculate(&mut self, ram: &mut SpuRam, input: [f32; 2]) { 50 | self.counter = (self.counter + 1) % 2; 51 | 52 | if self.counter == 1 { 53 | return; 54 | } 55 | 56 | for i in 0..2 { 57 | let mut msame = input[i] * i16_to_f32(self.vin[i]); 58 | msame += self.read(ram, self.dsame[i]) * i16_to_f32(self.vwall); 59 | msame -= self.read(ram, self.msame[i] - 2); 60 | msame *= i16_to_f32(self.viir); 61 | msame += self.read(ram, self.msame[i] - 2); 62 | self.write(ram, self.msame[i], msame); 63 | 64 | let mut mdiff = input[i] * i16_to_f32(self.vin[i]); 65 | mdiff += self.read(ram, self.ddiff[1 - i]) * i16_to_f32(self.vwall); 66 | mdiff -= self.read(ram, self.mdiff[i] - 2); 67 | mdiff *= i16_to_f32(self.viir); 68 | mdiff += self.read(ram, self.mdiff[i] - 2); 69 | self.write(ram, self.mdiff[i], mdiff); 70 | 71 | self.output[i] = i16_to_f32(self.vcomb1) * self.read(ram, self.mcomb1[i]); 72 | self.output[i] += i16_to_f32(self.vcomb2) * self.read(ram, self.mcomb2[i]); 73 | self.output[i] += i16_to_f32(self.vcomb3) * self.read(ram, self.mcomb3[i]); 74 | self.output[i] += i16_to_f32(self.vcomb4) * self.read(ram, self.mcomb4[i]); 75 | 76 | self.output[i] -= i16_to_f32(self.vapf1) * self.read(ram, self.mapf1[i] - self.dapf1); 77 | self.write(ram, self.mapf1[i], self.output[i]); 78 | self.output[i] = self.output[i] * i16_to_f32(self.vapf1) 79 | + self.read(ram, self.mapf1[i] - self.dapf1); 80 | 81 | self.output[i] -= i16_to_f32(self.vapf2) * self.read(ram, self.mapf2[i] - self.dapf2); 82 | self.write(ram, self.mapf2[i], self.output[i]); 83 | self.output[i] = self.output[i] * i16_to_f32(self.vapf2) 84 | + self.read(ram, self.mapf2[i] - self.dapf2); 85 | } 86 | 87 | self.buffer_address = cmp::max(self.mbase, (self.buffer_address + 2) & 0x7fffe); 88 | } 89 | 90 | fn read(&self, ram: &mut SpuRam, address: u32) -> f32 { 91 | let sample = ram.memory_read16(self.calc_addr(address)) as i16; 92 | i16_to_f32(sample) 93 | } 94 | 95 | fn write(&self, ram: &mut SpuRam, address: u32, value: f32) { 96 | let sample = f32_to_i16(clip(value, -1.0, 1.0)); 97 | ram.memory_write16(self.calc_addr(address), sample as u16); 98 | } 99 | 100 | fn calc_addr(&self, address: u32) -> u32 { 101 | let mut offset = self.buffer_address + address - self.mbase; 102 | offset %= 0x80000 - self.mbase; 103 | 104 | (self.mbase + offset) & 0x7fffe 105 | } 106 | 107 | pub fn output_l(&self) -> f32 { 108 | clip(self.output[0], -1.0, 1.0) 109 | } 110 | 111 | pub fn output_r(&self) -> f32 { 112 | clip(self.output[1], -1.0, 1.0) 113 | } 114 | 115 | pub fn get_base(&self) -> u16 { 116 | (self.mbase / 8) as u16 117 | } 118 | 119 | pub fn set_base(&mut self, value: u16) { 120 | self.mbase = (value as u32) * 8; 121 | self.buffer_address = (value as u32) * 8; 122 | } 123 | 124 | pub fn read16(&self, address: u32) -> u16 { 125 | match address { 126 | _ => panic!( 127 | "[SPU] [ERROR] Read from invalid reverb register: 0x{:08x}", 128 | address 129 | ), 130 | } 131 | } 132 | 133 | pub fn write16(&mut self, address: u32, value: u16) { 134 | match address { 135 | 0x1f801dc0 => self.dapf1 = (value as u32) * 8, 136 | 0x1f801dc2 => self.dapf2 = (value as u32) * 8, 137 | 0x1f801dc4 => self.viir = value as i16, 138 | 0x1f801dc6 => self.vcomb1 = value as i16, 139 | 0x1f801dc8 => self.vcomb2 = value as i16, 140 | 0x1f801dca => self.vcomb3 = value as i16, 141 | 0x1f801dcc => self.vcomb4 = value as i16, 142 | 0x1f801dce => self.vwall = value as i16, 143 | 0x1f801dd0 => self.vapf1 = value as i16, 144 | 0x1f801dd2 => self.vapf2 = value as i16, 145 | 0x1f801dd4 => self.msame[0] = (value as u32) * 8, 146 | 0x1f801dd6 => self.msame[1] = (value as u32) * 8, 147 | 0x1f801dd8 => self.mcomb1[0] = (value as u32) * 8, 148 | 0x1f801dda => self.mcomb1[1] = (value as u32) * 8, 149 | 0x1f801ddc => self.mcomb2[0] = (value as u32) * 8, 150 | 0x1f801dde => self.mcomb2[1] = (value as u32) * 8, 151 | 0x1f801de0 => self.dsame[0] = (value as u32) * 8, 152 | 0x1f801de2 => self.dsame[1] = (value as u32) * 8, 153 | 0x1f801de4 => self.mdiff[0] = (value as u32) * 8, 154 | 0x1f801de6 => self.mdiff[1] = (value as u32) * 8, 155 | 0x1f801de8 => self.mcomb3[0] = (value as u32) * 8, 156 | 0x1f801dea => self.mcomb3[1] = (value as u32) * 8, 157 | 0x1f801dec => self.mcomb4[0] = (value as u32) * 8, 158 | 0x1f801dee => self.mcomb4[1] = (value as u32) * 8, 159 | 0x1f801df0 => self.ddiff[0] = (value as u32) * 8, 160 | 0x1f801df2 => self.ddiff[1] = (value as u32) * 8, 161 | 0x1f801df4 => self.mapf1[0] = (value as u32) * 8, 162 | 0x1f801df6 => self.mapf1[1] = (value as u32) * 8, 163 | 0x1f801df8 => self.mapf2[0] = (value as u32) * 8, 164 | 0x1f801dfa => self.mapf2[1] = (value as u32) * 8, 165 | 0x1f801dfc => self.vin[0] = value as i16, 166 | 0x1f801dfe => self.vin[1] = value as i16, 167 | _ => panic!( 168 | "[SPU] [ERROR] Write to invalid reverb register: 0x{:08x}", 169 | address 170 | ), 171 | }; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/psx/spu/voice.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::util::{clip, f32_to_i16, i16_to_f32}; 6 | 7 | use crate::psx::adpcm::ADPCM_FILTERS; 8 | 9 | use super::adsr::{Adsr, AdsrState}; 10 | use super::gauss::GAUSS_TABLE; 11 | use super::volume::Volume; 12 | use super::SpuRam; 13 | 14 | pub const VOICE_SIZE: usize = 0x10; 15 | pub const NR_SAMPLES: usize = 28; 16 | 17 | #[derive(Clone, Copy, Deserialize, Serialize)] 18 | pub struct Voice { 19 | counter: usize, 20 | 21 | volume: Volume, 22 | 23 | pitch: u16, 24 | pub modulator: i16, 25 | 26 | start_address: u32, 27 | repeat_address: u32, 28 | current_address: u32, 29 | 30 | repeat_address_written: bool, 31 | 32 | endx: bool, 33 | reverb: bool, 34 | noise: bool, 35 | 36 | adsr: Adsr, 37 | 38 | samples: [i16; NR_SAMPLES], 39 | prev_samples: [i16; 2], 40 | last_samples: [i16; 4], 41 | } 42 | 43 | impl Voice { 44 | pub fn new() -> Voice { 45 | Voice { 46 | counter: 0, 47 | 48 | volume: Volume::default(), 49 | 50 | pitch: 0, 51 | modulator: 0, 52 | 53 | start_address: 0, 54 | repeat_address: 0, 55 | current_address: 0, 56 | 57 | repeat_address_written: false, 58 | 59 | endx: false, 60 | reverb: false, 61 | noise: false, 62 | 63 | adsr: Adsr::default(), 64 | 65 | samples: [0; NR_SAMPLES], 66 | prev_samples: [0; 2], 67 | last_samples: [0; 4], 68 | } 69 | } 70 | 71 | pub fn from_address(address: u32) -> (usize, usize) { 72 | let voice = (address & 0x1f0) >> 4; 73 | let offset = address & 0xf; 74 | 75 | (voice as usize, offset as usize) 76 | } 77 | 78 | pub fn disabled(&self) -> bool { 79 | return self.adsr.state == AdsrState::Disabled; 80 | } 81 | 82 | pub fn reverb_enabled(&self) -> bool { 83 | self.reverb 84 | } 85 | 86 | pub fn key_on(&mut self) { 87 | self.adsr.state = AdsrState::Attack; 88 | self.adsr.volume = 0; 89 | self.adsr.cycles = 0; 90 | 91 | self.endx = false; 92 | 93 | self.current_address = self.start_address; 94 | 95 | if !self.repeat_address_written { 96 | self.repeat_address = self.start_address; 97 | } 98 | 99 | self.repeat_address_written = false; 100 | } 101 | 102 | pub fn key_off(&mut self) { 103 | self.adsr.state = AdsrState::Release; 104 | self.adsr.cycles = 0; 105 | } 106 | 107 | pub fn endx(&mut self) -> bool { 108 | self.endx 109 | } 110 | 111 | pub fn set_noise(&mut self, state: bool) { 112 | self.noise = state; 113 | } 114 | 115 | pub fn echo_on(&mut self) { 116 | self.reverb = true; 117 | } 118 | 119 | fn sample_index(&self) -> usize { 120 | self.counter >> 12 121 | } 122 | 123 | fn gauss_index(&self) -> usize { 124 | (self.counter & 0xff0) >> 4 125 | } 126 | 127 | fn get_sample(&self, index: isize) -> i16 { 128 | if index < 0 { 129 | self.last_samples[(index + 4) as usize] 130 | } else { 131 | self.samples[index as usize] 132 | } 133 | } 134 | 135 | fn interpolate(&self, index: isize) -> f32 { 136 | let gauss_index = self.gauss_index(); 137 | 138 | let s1 = self.get_sample(index - 3) as i32; 139 | let s2 = self.get_sample(index - 2) as i32; 140 | let s3 = self.get_sample(index - 1) as i32; 141 | let s4 = self.get_sample(index - 0) as i32; 142 | 143 | let mut out = 0; 144 | out += (GAUSS_TABLE[0x0ff - gauss_index] * s1) >> 15; 145 | out += (GAUSS_TABLE[0x1ff - gauss_index] * s2) >> 15; 146 | out += (GAUSS_TABLE[0x100 + gauss_index] * s3) >> 15; 147 | out += (GAUSS_TABLE[0x000 + gauss_index] * s4) >> 15; 148 | 149 | i16_to_f32(out as i16) 150 | } 151 | 152 | pub fn get_samples(&mut self, noise: bool, noise_level: f32) -> (f32, f32) { 153 | let index = self.sample_index(); 154 | 155 | self.adsr.update(); 156 | 157 | let mut sample; 158 | 159 | if noise { 160 | sample = noise_level; 161 | } else { 162 | sample = self.interpolate(index as isize); 163 | } 164 | 165 | sample *= i16_to_f32(self.adsr.volume); 166 | 167 | self.modulator = f32_to_i16(sample); 168 | 169 | let left = sample * self.volume.l(); 170 | let right = sample * self.volume.r(); 171 | 172 | (left, right) 173 | } 174 | 175 | fn update_sample_index(&mut self) { 176 | let new = self.sample_index() - NR_SAMPLES; 177 | 178 | self.counter &= 0xfff; 179 | self.counter |= new << 12; 180 | } 181 | 182 | fn decode_samples(&mut self, ram: &mut SpuRam) { 183 | let header = ram.memory_read16(self.current_address); 184 | let flags = header >> 8; 185 | let filter = ((header & 0xf0) >> 4) as usize; 186 | let mut shift = header & 0xf; 187 | 188 | if shift > 12 { 189 | shift = 8; 190 | } 191 | 192 | if filter > 5 { 193 | println!("[SPU] [WARN] Invalid filter {}", filter); 194 | } 195 | 196 | if (flags & 0x4) != 0 { 197 | self.repeat_address = self.current_address; 198 | } 199 | 200 | for i in 0..7 { 201 | self.current_address += 2; 202 | self.current_address &= 0x7ffff; 203 | 204 | let mut samples = ram.memory_read16(self.current_address); 205 | 206 | for j in 0..4 { 207 | let mut sample = (samples << 12) as i16 as i32; 208 | sample >>= shift; 209 | 210 | let mut quant = 32; 211 | quant += self.prev_samples[0] as i32 * ADPCM_FILTERS[filter][0] as i32; 212 | quant -= self.prev_samples[1] as i32 * ADPCM_FILTERS[filter][1] as i32; 213 | 214 | sample = clip(sample + (quant / 64), -0x8000, 0x7fff); 215 | 216 | self.samples[i * 4 + j] = sample as i16; 217 | self.prev_samples[1] = self.prev_samples[0]; 218 | self.prev_samples[0] = sample as i16; 219 | 220 | samples >>= 4; 221 | } 222 | } 223 | 224 | self.current_address += 2; 225 | self.current_address &= 0x7ffff; 226 | 227 | if (flags & 0x1) != 0 { 228 | self.endx = true; 229 | self.current_address = self.repeat_address; 230 | 231 | if (flags & 0x2) == 0 && !self.noise { 232 | self.key_off(); 233 | self.adsr.volume = 0; 234 | } 235 | } 236 | } 237 | 238 | pub fn update(&mut self, ram: &mut SpuRam, modulate: bool, modulator: i16) { 239 | let mut step = self.pitch as u32; 240 | 241 | if modulate { 242 | let factor = (modulator as i32 + 0x8000) as u32; 243 | step = step as i16 as u32; 244 | step = (step * factor) >> 15; 245 | step &= 0xffff; 246 | } 247 | 248 | self.counter += cmp::min(step, 0x4000) as usize; 249 | 250 | self.reverb = false; 251 | 252 | if self.sample_index() >= NR_SAMPLES { 253 | self.update_sample_index(); 254 | 255 | self.last_samples[0] = self.samples[24]; 256 | self.last_samples[1] = self.samples[25]; 257 | self.last_samples[2] = self.samples[26]; 258 | self.last_samples[3] = self.samples[27]; 259 | 260 | self.decode_samples(ram); 261 | } 262 | } 263 | 264 | pub fn read16(&self, offset: usize) -> u16 { 265 | assert!(offset < VOICE_SIZE); 266 | 267 | match offset { 268 | 0x0 => (self.volume.left as u16) >> 1, 269 | 0x2 => (self.volume.right as u16) >> 1, 270 | 0x4 => self.pitch, 271 | 0x6 => (self.start_address / 8) as u16, 272 | 0x8 => self.adsr.config as u16, 273 | 0xa => (self.adsr.config >> 16) as u16, 274 | 0xc => self.adsr.volume as u16, 275 | 0xe => (self.repeat_address / 8) as u16, 276 | _ => panic!( 277 | "[SPU] [ERROR] Read from invalid voice register: 0x{:x}", 278 | offset 279 | ), 280 | } 281 | } 282 | 283 | pub fn write16(&mut self, offset: usize, value: u16) { 284 | assert!(offset < VOICE_SIZE); 285 | 286 | match offset { 287 | 0x0 => { 288 | if (value & 0x8000) != 0 { 289 | println!("[SPU] [WARN] Sweep enabled for left channel"); 290 | } 291 | 292 | self.volume.left = (value << 1) as i16; 293 | } 294 | 0x2 => { 295 | if (value & 0x8000) != 0 { 296 | println!("[SPU] [WARN] Sweep enabled for right channel"); 297 | } 298 | 299 | self.volume.right = (value << 1) as i16; 300 | } 301 | 0x4 => self.pitch = value, 302 | 0x6 => self.start_address = (value as u32) * 8, 303 | 0x8 => { 304 | self.adsr.config &= 0xffff0000; 305 | self.adsr.config |= value as u32; 306 | } 307 | 0xa => { 308 | self.adsr.config &= 0xffff; 309 | self.adsr.config |= (value as u32) << 16; 310 | } 311 | 0xc => self.adsr.volume = value as i16, 312 | 0xe => { 313 | self.repeat_address = (value as u32) * 8; 314 | self.repeat_address_written = true; 315 | }, 316 | _ => panic!( 317 | "[SPU] [ERROR] Write to invalid voice register: 0x{:x}", 318 | offset 319 | ), 320 | }; 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/psx/spu/volume.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::util::i16_to_f32; 4 | 5 | #[derive(Clone, Copy, Default, Deserialize, Serialize)] 6 | pub struct Volume { 7 | pub left: i16, 8 | pub right: i16, 9 | } 10 | 11 | impl Volume { 12 | pub fn l(self) -> f32 { 13 | i16_to_f32(self.left) 14 | } 15 | 16 | pub fn r(self) -> f32 { 17 | i16_to_f32(self.right) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/psx/timekeeper.rs: -------------------------------------------------------------------------------- 1 | use super::bus::Bus; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | const DEVICE_COUNT: usize = 5; 6 | const DEVICE_GRANULARITY: [u64; DEVICE_COUNT] = [7, 8448, 8448, 11, 11]; 7 | 8 | const DMAC_GRANULARITY: u64 = 11; 9 | 10 | #[derive(Clone, Copy)] 11 | pub enum Device { 12 | Gpu, 13 | Cdrom, 14 | Spu, 15 | Timers, 16 | Sio0, 17 | } 18 | 19 | impl Device { 20 | pub fn from(value: usize) -> Device { 21 | match value { 22 | 0 => Device::Gpu, 23 | 1 => Device::Cdrom, 24 | 2 => Device::Spu, 25 | 3 => Device::Timers, 26 | 4 => Device::Sio0, 27 | _ => unreachable!(), 28 | } 29 | } 30 | } 31 | 32 | #[derive(Deserialize, Serialize)] 33 | pub struct Timekeeper { 34 | now: u64, 35 | last_sync: u64, 36 | 37 | devices: [u64; DEVICE_COUNT], 38 | dmac: u64, 39 | } 40 | 41 | impl Timekeeper { 42 | pub fn new() -> Timekeeper { 43 | Timekeeper { 44 | now: 0, 45 | last_sync: 0, 46 | 47 | devices: [0; DEVICE_COUNT], 48 | dmac: 0, 49 | } 50 | } 51 | 52 | pub fn reset(&mut self) { 53 | self.now = 0; 54 | self.last_sync = 0; 55 | 56 | self.devices = [0; DEVICE_COUNT]; 57 | self.dmac = 0; 58 | } 59 | 60 | pub fn tick(&mut self, cycles: u64) { 61 | self.now += cycles * 11; 62 | } 63 | 64 | pub fn sync_all(&mut self, bus: &mut Bus) { 65 | self.last_sync = self.now; 66 | 67 | for i in 0..DEVICE_COUNT { 68 | self.sync_device(bus, Device::from(i)); 69 | } 70 | } 71 | 72 | pub fn sync_device(&mut self, bus: &mut Bus, device: Device) { 73 | let elapsed = self.now - self.devices[device as usize]; 74 | let cycles = elapsed / DEVICE_GRANULARITY[device as usize]; 75 | 76 | self.devices[device as usize] += cycles * DEVICE_GRANULARITY[device as usize]; 77 | bus.tick_device_by_id(device, cycles as usize); 78 | } 79 | 80 | pub fn sync_dmac(&mut self) -> usize { 81 | let elapsed = self.now - self.dmac; 82 | let cycles = elapsed / DMAC_GRANULARITY; 83 | 84 | self.dmac += cycles * DMAC_GRANULARITY; 85 | cycles as usize 86 | } 87 | 88 | pub fn elapsed(&self) -> u64 { 89 | (self.now - self.last_sync) / 11 90 | } 91 | } -------------------------------------------------------------------------------- /src/psx/timers.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use super::intc::{Intc, Interrupt}; 6 | 7 | #[derive(Deserialize, Serialize)] 8 | pub struct Counter { 9 | value: u32, 10 | mode: u32, 11 | target: u32, 12 | 13 | div8counter: usize, 14 | } 15 | 16 | impl Counter { 17 | pub fn new() -> Counter { 18 | Counter { 19 | value: 0, 20 | mode: 0, 21 | target: 0, 22 | 23 | div8counter: 0, 24 | } 25 | } 26 | 27 | pub fn tick(&mut self, intc: &mut Intc, number: usize, clocks: usize) { 28 | let timer = match number { 29 | 0 => Interrupt::Tmr0, 30 | 1 => Interrupt::Tmr1, 31 | 2 => Interrupt::Tmr2, 32 | _ => unreachable!(), 33 | }; 34 | 35 | let mut irq = false; 36 | 37 | if (self.mode & 0x8) != 0 && self.target == 0 && self.value == 0 { 38 | self.mode |= 0x800; 39 | 40 | if (self.mode & 0x8) != 0 { 41 | self.value %= cmp::max(1, self.target); 42 | } 43 | 44 | if (self.mode & 0x10) != 0 { 45 | intc.assert_irq(timer); 46 | } 47 | 48 | return; 49 | } 50 | 51 | let old_value = self.value; 52 | self.value += clocks as u32; 53 | 54 | if (old_value < self.target && self.value >= self.target) 55 | || (self.value >= self.target + 0x10000) 56 | { 57 | self.mode |= 0x800; 58 | 59 | if (self.mode & 0x8) != 0 { 60 | self.value %= cmp::max(1, self.target); 61 | } 62 | 63 | if (self.mode & 0x10) != 0 { 64 | irq = true; 65 | } 66 | } 67 | 68 | if self.value > 0xffff { 69 | self.mode |= 0x1000; 70 | 71 | self.value &= 0xffff; 72 | 73 | if (self.mode & 0x20) != 0 { 74 | irq = true; 75 | } 76 | } 77 | 78 | irq &= (self.mode & 0x400) != 0; 79 | 80 | if irq && ((self.mode & 0x80) != 0) { 81 | self.mode ^= 0x400; 82 | } 83 | 84 | if (self.mode & 0x40) == 0 { 85 | self.mode &= !0x400; 86 | } 87 | 88 | if irq { 89 | intc.assert_irq(timer); 90 | } 91 | } 92 | } 93 | 94 | #[derive(Deserialize, Serialize)] 95 | pub struct Timers { 96 | counter: [Counter; 3], 97 | 98 | vblank: bool, 99 | hblank: bool, 100 | } 101 | 102 | impl Timers { 103 | pub fn new() -> Timers { 104 | Timers { 105 | counter: [ 106 | Counter::new(), 107 | Counter::new(), 108 | Counter::new(), 109 | ], 110 | 111 | vblank: false, 112 | hblank: false, 113 | } 114 | } 115 | 116 | pub fn read(&mut self, address: u32) -> u32 { 117 | let index = ((address & 0x30) >> 4) as usize; 118 | let section = address & 0xc; 119 | 120 | let counter = &mut self.counter[index]; 121 | 122 | match section { 123 | 0 => counter.value, 124 | 4 => { 125 | let mode = counter.mode; 126 | counter.mode &= !0x1000; 127 | 128 | if counter.value != counter.target { 129 | counter.mode &= !0x800; 130 | } 131 | mode 132 | } 133 | 8 => counter.target & 0xffff, 134 | _ => panic!( 135 | "[TIMER] [ERROR] Read from unrecognised address {:#x}", 136 | address 137 | ), 138 | } 139 | } 140 | 141 | pub fn write(&mut self, address: u32, value: u32) { 142 | let index = ((address & 0x30) >> 4) as usize; 143 | let section = address & 0xc; 144 | 145 | let counter = &mut self.counter[index]; 146 | 147 | match section { 148 | 0 => counter.value = value & 0xffff, 149 | 4 => { 150 | counter.mode = (value & 0x3ff) | (counter.mode & 0x1c00); 151 | counter.mode |= 0x400; 152 | counter.value = 0; 153 | 154 | if (value & 0x1) != 0 { 155 | println!( 156 | "[TIMER] [INFO] Setting sync mode to {} for timer #{}", 157 | (value & 0x6) >> 1, 158 | index 159 | ); 160 | } 161 | 162 | if (value & 0x300) != 0 { 163 | println!( 164 | "[TIMER] [INFO] Setting clock source to {} for timer #{}", 165 | (value & 0x300) >> 8, 166 | index 167 | ); 168 | } 169 | } 170 | 8 => counter.target = value & 0xffff, 171 | _ => panic!( 172 | "[TIMER] [ERROR] Read from unrecognised address {:#x}", 173 | address 174 | ), 175 | } 176 | } 177 | 178 | pub fn tick(&mut self, intc: &mut Intc, clocks: usize) { 179 | self.tick0(intc, clocks); 180 | self.tick1(intc, clocks); 181 | self.tick2(intc, clocks); 182 | } 183 | 184 | pub fn tick0(&mut self, intc: &mut Intc, clocks: usize) { 185 | let counter = &mut self.counter[0]; 186 | 187 | if (counter.mode & 0x100) == 0 { 188 | counter.tick(intc, 0, clocks); 189 | } 190 | } 191 | 192 | pub fn tick1(&mut self, intc: &mut Intc, clocks: usize) { 193 | let counter = &mut self.counter[1]; 194 | 195 | if (counter.mode & 0x7) == 0x7 { 196 | return; 197 | } 198 | 199 | if (counter.mode & 0x100) == 0 { 200 | counter.tick(intc, 1, clocks); 201 | } 202 | } 203 | 204 | pub fn tick2(&mut self, intc: &mut Intc, mut clocks: usize) { 205 | let counter = &mut self.counter[2]; 206 | 207 | if (counter.mode & 0x200) != 0 { 208 | counter.div8counter += clocks; 209 | clocks = counter.div8counter >> 3; 210 | counter.div8counter &= 0x7; 211 | } 212 | 213 | if (counter.mode & 0x1) != 0 { 214 | clocks = 0; 215 | } 216 | 217 | counter.tick(intc, 2, clocks); 218 | } 219 | 220 | pub fn tick_dotclock(&mut self, intc: &mut Intc, clocks: usize) { 221 | let counter = &mut self.counter[0]; 222 | 223 | if (counter.mode & 0x100) != 0 { 224 | counter.tick(intc, 0, clocks); 225 | } 226 | } 227 | 228 | pub fn tick_hblank(&mut self, intc: &mut Intc) { 229 | let counter = &mut self.counter[1]; 230 | 231 | if (counter.mode & 0x7) == 0x7 { 232 | return; 233 | } 234 | 235 | if (counter.mode & 0x100) != 0 { 236 | counter.tick(intc, 1, 1); 237 | } 238 | } 239 | 240 | pub fn set_vblank(&mut self, state: bool) { 241 | self.vblank = state; 242 | 243 | if self.vblank && (self.counter[1].mode & 0x7) == 0x7 { 244 | self.counter[1].mode &= !0x1; 245 | } 246 | } 247 | 248 | pub fn set_hblank(&mut self, state: bool) { 249 | self.hblank = state; 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/queue.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize)] 4 | pub struct Queue { 5 | data: Vec, 6 | capacity: usize, 7 | } 8 | 9 | impl Queue { 10 | pub fn push(&mut self, value: T) { 11 | if !self.full() { 12 | self.data.push(value); 13 | } 14 | } 15 | 16 | pub fn len(&self) -> usize { 17 | self.data.len() 18 | } 19 | 20 | pub fn is_empty(&self) -> bool { 21 | self.data.is_empty() 22 | } 23 | 24 | pub fn has_data(&self) -> bool { 25 | !self.data.is_empty() 26 | } 27 | 28 | pub fn full(&self) -> bool { 29 | self.data.len() >= self.capacity 30 | } 31 | 32 | pub fn has_space(&self) -> bool { 33 | self.data.len() < self.capacity 34 | } 35 | 36 | pub fn clear(&mut self) { 37 | self.data.clear(); 38 | } 39 | 40 | pub fn data(&mut self) -> &mut Vec { 41 | &mut self.data 42 | } 43 | } 44 | 45 | impl Queue { 46 | pub fn new(capacity: usize) -> Queue { 47 | Queue { 48 | data: Vec::with_capacity(capacity), 49 | capacity: capacity, 50 | } 51 | } 52 | 53 | pub fn pop(&mut self) -> u8 { 54 | if self.has_data() { 55 | return self.data.remove(0); 56 | } else { 57 | return 0; 58 | } 59 | } 60 | } 61 | 62 | impl Queue { 63 | pub fn new(capacity: usize) -> Queue { 64 | Queue { 65 | data: Vec::with_capacity(capacity), 66 | capacity: capacity, 67 | } 68 | } 69 | 70 | pub fn pop(&mut self) -> u16 { 71 | if self.has_data() { 72 | return self.data.remove(0); 73 | } else { 74 | return 0; 75 | } 76 | } 77 | } 78 | 79 | impl Queue { 80 | pub fn new(capacity: usize) -> Queue { 81 | Queue { 82 | data: Vec::with_capacity(capacity), 83 | capacity: capacity, 84 | } 85 | } 86 | 87 | pub fn pop(&mut self) -> u32 { 88 | if self.has_data() { 89 | return self.data.remove(0); 90 | } else { 91 | return 0; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::fs::File; 3 | use std::io::{self, Read}; 4 | use std::path::Path; 5 | 6 | use byteorder::{ByteOrder, LittleEndian}; 7 | 8 | pub fn bcd_to_u8(value: u8) -> u8 { 9 | ((value >> 4) * 10) + (value & 0xf) 10 | } 11 | 12 | pub fn u8_to_bcd(value: u8) -> u8 { 13 | ((value / 10) << 4) | (value % 10) 14 | } 15 | 16 | pub fn i16_to_f32(value: i16) -> f32 { 17 | if value >= 0 { 18 | f32::from(value) / f32::from(i16::max_value()) 19 | } else { 20 | -f32::from(value) / f32::from(i16::min_value()) 21 | } 22 | } 23 | 24 | pub fn f32_to_i16(value: f32) -> i16 { 25 | if value >= 0.0 { 26 | (value * f32::from(i16::max_value())) as i16 27 | } else { 28 | (-value * f32::from(i16::min_value())) as i16 29 | } 30 | } 31 | 32 | pub fn sign_extend_u16(mut value: u16, size: usize) -> u16 { 33 | let sign = 1 << (size - 1); 34 | let mask = (1 << size) - 1; 35 | 36 | if (value & sign) != 0 { 37 | value |= !mask; 38 | } else { 39 | value &= mask; 40 | } 41 | 42 | return value; 43 | } 44 | 45 | pub fn sign_extend_i32(mut value: i32, size: usize) -> i32 { 46 | let sign = 1 << (size - 1); 47 | let mask = (1 << size) - 1; 48 | 49 | if (value & sign) != 0 { 50 | value |= !mask; 51 | } else { 52 | value &= mask; 53 | } 54 | 55 | return value; 56 | } 57 | 58 | pub fn clip(value: T, min: T, max: T) -> T { 59 | if value <= min { 60 | return min; 61 | } 62 | 63 | if value >= max { 64 | return max; 65 | } 66 | 67 | return value; 68 | } 69 | 70 | pub fn min3(a: T, b: T, c: T) -> T { 71 | cmp::min(a, cmp::min(b, c)) 72 | } 73 | 74 | pub fn max3(a: T, b: T, c: T) -> T { 75 | cmp::max(a, cmp::max(b, c)) 76 | } 77 | 78 | pub fn read_file_to_box(filepath: &str) -> Box<[u8]> { 79 | let path = Path::new(filepath); 80 | 81 | if !path.is_file() { 82 | panic!("ERROR: file does not exist: {}", path.display()) 83 | } 84 | 85 | let mut file = File::open(path).unwrap(); 86 | let mut file_buffer = Vec::new(); 87 | 88 | file.read_to_end(&mut file_buffer).unwrap(); 89 | 90 | file_buffer.into_boxed_slice() 91 | } 92 | 93 | pub fn discard(file: &mut File, size: usize) -> io::Result<()> { 94 | let mut buffer = vec![0; size]; 95 | file.read_exact(&mut buffer)?; 96 | 97 | Ok(()) 98 | } 99 | 100 | pub fn read_to_buffer(file: &mut File, size: usize) -> io::Result> { 101 | let mut buffer = vec![0; size]; 102 | file.read_exact(&mut buffer)?; 103 | 104 | Ok(buffer) 105 | } 106 | 107 | pub fn read_u8(file: &mut File) -> io::Result { 108 | Ok(read_to_buffer(file, 1)?[0]) 109 | } 110 | 111 | pub fn read_u32(file: &mut File) -> io::Result { 112 | Ok(LittleEndian::read_u32(&read_to_buffer(file, 4)?)) 113 | } --------------------------------------------------------------------------------