trapezoid_core/
memory.rs

1mod dma;
2mod expansion_regions;
3pub(crate) mod interrupts;
4mod memory_control;
5mod ram;
6
7use std::fs::File;
8use std::io::{Read, Seek, SeekFrom};
9use std::path::Path;
10use std::sync::Arc;
11
12use crate::gpu::{Device, Queue};
13use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
14
15use crate::cdrom::Cdrom;
16use crate::controller_mem_card::ControllerAndMemoryCard;
17use crate::cpu::CpuBusProvider;
18use crate::gpu::Gpu;
19use crate::mdec::Mdec;
20use crate::spu::Spu;
21use crate::timers::Timers;
22use crate::{PsxConfig, PsxError};
23
24use dma::Dma;
25use expansion_regions::{ExpansionRegion1, ExpansionRegion2};
26use interrupts::Interrupts;
27use memory_control::{CacheControl, MemoryControl1, MemoryControl2};
28use ram::{MainRam, Scratchpad};
29
30pub type Result<T, E = String> = std::result::Result<T, E>;
31
32/// A notion of a Busline, which is an interface to interact with memory mapped devices
33/// it can be a memory, or other stuff.
34///
35/// Here we implement the behavior for `read/write` for each size of data (u32, u16, u8).
36pub trait BusLine {
37    fn read_u32(&mut self, addr: u32) -> Result<u32> {
38        Err(format!(
39            "{}: u32 read from {:08X}",
40            std::any::type_name::<Self>(),
41            addr
42        ))
43    }
44
45    fn write_u32(&mut self, addr: u32, _data: u32) -> Result<()> {
46        Err(format!(
47            "{}: u32 write to {:08X}",
48            std::any::type_name::<Self>(),
49            addr
50        ))
51    }
52
53    fn read_u16(&mut self, addr: u32) -> Result<u16> {
54        Err(format!(
55            "{}: u16 read from {:08X}",
56            std::any::type_name::<Self>(),
57            addr
58        ))
59    }
60    fn write_u16(&mut self, addr: u32, _data: u16) -> Result<()> {
61        Err(format!(
62            "{}: u16 write to {:08X}",
63            std::any::type_name::<Self>(),
64            addr
65        ))
66    }
67
68    fn read_u8(&mut self, addr: u32) -> Result<u8> {
69        Err(format!(
70            "{}: u8 read from {:08X}",
71            std::any::type_name::<Self>(),
72            addr
73        ))
74    }
75    fn write_u8(&mut self, addr: u32, _data: u8) -> Result<()> {
76        Err(format!(
77            "{}: u8 write to {:08X}",
78            std::any::type_name::<Self>(),
79            addr
80        ))
81    }
82}
83
84pub struct Bios {
85    data: Vec<u8>,
86}
87
88impl Bios {
89    fn write_u32(&mut self, addr: u32, data: u32) {
90        let index = (addr & 0xFFFFF) as usize;
91
92        LittleEndian::write_u32(&mut self.data[index..index + 4], data)
93    }
94
95    fn apply_patches(&mut self) {
96        // patch to support TTY
97        // the BIOS by default hardcode disable the TTY driver, here we change it
98        // from writing 0 to 1 in order to enable the driver load
99        if self.read_u32(0x6f0c).unwrap() == 0x3C01A001
100            && self.read_u32(0x6f14).unwrap() == 0xAC20B9B0
101        {
102            self.write_u32(0x6f0c, 0x34010001);
103            self.write_u32(0x6f14, 0xAF81A9C0);
104        }
105        // patch to fix a bug where the cursor of the controller is blinking
106        //
107        // The problem is that the BIOS does this when getting the digital switches data:
108        //     while (I_STAT & 0x80 == 0) {
109        //         if (JOY_STAT & 2 != 0) {
110        //             goto save_input_and_continue;
111        //         }
112        //     }
113        //     while (JOY_STAT & 2 == 0) {}
114        //     // return value for "controller not connected"
115        //     return 0xFFFF;
116        //
117        // Which will save the input and continue *only and only if* it read the
118        // `controller_and_mem_card` interrupt from `I_STAT` first then read the
119        // `RX_FIFO_NOT_EMPTY` from `JOY_STAT`. If it read it the other way around
120        // (meaning that the transfer finished just after the read of `JOY_STAT`
121        // inside the loop, then it will report as `controller not connected`.
122        //
123        // This problem happens in some frames and results in blinking cursor.
124        //
125        // The patch fixes this issue by modifing the jump after the first loop
126        // to the `save_input_and_continue` address. It was also tested, switches
127        // in the controller works without any problems and the BIOS can read
128        // the keys, only the blinking is fixed.
129        if self.read_u32(0x14330).unwrap() == 0x92200000
130            && self.read_u32(0x14334).unwrap() == 0x10000047
131            && self.read_u32(0x14338).unwrap() == 0x8fae0040
132        {
133            self.write_u32(0x14330, 0x00000000);
134            self.write_u32(0x14334, 0x10000006);
135            self.write_u32(0x14338, 0x00000000);
136        }
137    }
138}
139
140impl Bios {
141    // TODO: produce a valid `Error` struct
142    pub fn from_file<P: AsRef<Path>>(bios_file_path: P) -> Result<Self, PsxError> {
143        let mut data = Vec::new();
144
145        let mut file = File::open(bios_file_path).map_err(|_| PsxError::CouldNotLoadBios)?;
146
147        file.read_to_end(&mut data)
148            .map_err(|_| PsxError::CouldNotLoadBios)?;
149
150        let mut s = Self { data };
151
152        s.apply_patches();
153
154        Ok(s)
155    }
156
157    pub fn read_u32(&self, addr: u32) -> Result<u32> {
158        let index = (addr & 0xFFFFF) as usize;
159
160        Ok(LittleEndian::read_u32(&self.data[index..index + 4]))
161    }
162
163    pub fn read_u16(&self, addr: u32) -> Result<u16> {
164        let index = (addr & 0xFFFFF) as usize;
165
166        Ok(LittleEndian::read_u16(&self.data[index..index + 4]))
167    }
168
169    pub fn read_u8(&self, addr: u32) -> Result<u8> {
170        let index = (addr & 0xFFFFF) as usize;
171
172        Ok(self.data[index])
173    }
174}
175
176/// A structure that holds the elements of the emulator that the DMA can access
177///
178/// These are the elements access by the channels:
179/// 0 & 1- MDEC
180/// 2- GPU
181/// 3- CDROM
182/// 4- SPU
183/// 5- PIO
184/// 6- OTC (GPU)
185///
186/// And also the main ram to write/read to/from.
187///
188/// The reason for this design, is to be able to pass this structure `&mut`
189/// to `Dma` without problems of double mut.
190struct DmaBus {
191    pub main_ram: MainRam,
192    pub cdrom: Cdrom,
193    pub gpu: Gpu,
194    pub mdec: Mdec,
195    pub spu: Spu,
196}
197
198pub struct CpuBus {
199    bios: Bios,
200    mem_ctrl_1: MemoryControl1,
201    mem_ctrl_2: MemoryControl2,
202    cache_control: CacheControl,
203    interrupts: Interrupts,
204    controller_mem_card: ControllerAndMemoryCard,
205
206    expansion_region_1: ExpansionRegion1,
207    expansion_region_2: ExpansionRegion2,
208
209    timers: Timers,
210
211    dma: Dma,
212    dma_bus: DmaBus,
213
214    scratchpad: Scratchpad,
215    config: PsxConfig,
216}
217
218impl CpuBus {
219    pub fn new<DiskPath: AsRef<Path>>(
220        bios: Bios,
221        disk_file: Option<DiskPath>,
222        config: PsxConfig,
223        device: Arc<Device>,
224        queue: Arc<Queue>,
225    ) -> Result<Self, PsxError> {
226        let mut s = Self {
227            bios,
228            mem_ctrl_1: MemoryControl1::default(),
229            mem_ctrl_2: MemoryControl2::default(),
230            cache_control: CacheControl::default(),
231            interrupts: Interrupts::default(),
232            controller_mem_card: ControllerAndMemoryCard::default(),
233
234            expansion_region_1: ExpansionRegion1::default(),
235            expansion_region_2: ExpansionRegion2::new(config),
236            dma: Dma::default(),
237
238            timers: Timers::default(),
239
240            dma_bus: DmaBus {
241                cdrom: Cdrom::default(),
242                gpu: Gpu::new(device, queue),
243                main_ram: MainRam::default(),
244                mdec: Mdec::default(),
245                spu: Spu::default(),
246            },
247
248            scratchpad: Scratchpad::default(),
249            config,
250        };
251
252        // TODO: handle errors in loading
253        if let Some(disk_file) = disk_file {
254            let path = disk_file.as_ref().to_owned();
255            // if this is an exe file
256            match path
257                .extension()
258                .unwrap()
259                .to_str()
260                .unwrap()
261                .to_ascii_lowercase()
262                .as_str()
263            {
264                "cue" => s.dma_bus.cdrom.set_cue_file(disk_file)?,
265                _ => {
266                    return Err(PsxError::DiskTypeNotSupported);
267                }
268            }
269        }
270
271        Ok(s)
272    }
273
274    pub fn reset(&mut self) {
275        self.mem_ctrl_1 = MemoryControl1::default();
276        self.mem_ctrl_2 = MemoryControl2::default();
277        self.cache_control = CacheControl::default();
278        self.interrupts = Interrupts::default();
279        self.controller_mem_card = ControllerAndMemoryCard::default();
280
281        self.expansion_region_1 = ExpansionRegion1::default();
282        self.expansion_region_2 = ExpansionRegion2::new(self.config);
283        self.dma = Dma::default();
284
285        self.timers = Timers::default();
286
287        self.dma_bus.cdrom.reset();
288        self.dma_bus.gpu.reset();
289        self.dma_bus.main_ram = MainRam::default();
290        self.dma_bus.mdec = Mdec::default();
291        self.dma_bus.spu = Spu::default();
292
293        self.scratchpad = Scratchpad::default();
294    }
295
296    pub fn gpu(&self) -> &Gpu {
297        &self.dma_bus.gpu
298    }
299
300    pub fn gpu_mut(&mut self) -> &mut Gpu {
301        &mut self.dma_bus.gpu
302    }
303
304    pub fn controller_mem_card_mut(&mut self) -> &mut ControllerAndMemoryCard {
305        &mut self.controller_mem_card
306    }
307
308    pub fn spu(&self) -> &Spu {
309        &self.dma_bus.spu
310    }
311
312    pub fn spu_mut(&mut self) -> &mut Spu {
313        &mut self.dma_bus.spu
314    }
315
316    pub fn cdrom_mut(&mut self) -> &mut Cdrom {
317        &mut self.dma_bus.cdrom
318    }
319}
320
321impl CpuBus {
322    // TODO: handle errors
323    //
324    /// Returns the metadata of the loaded exe
325    pub fn load_exe_in_memory<P: AsRef<Path>>(&mut self, exe_file_path: P) -> (u32, u32, u32) {
326        let mut file = File::open(exe_file_path).unwrap();
327        let mut magic = [0; 8];
328        let mut data = Vec::new();
329
330        file.read_exact(&mut magic).unwrap();
331        assert!(&magic == b"PS-X EXE");
332        // bytes from 0x8 to 0xF
333        assert!(file.read_u64::<LittleEndian>().unwrap() == 0);
334
335        let initial_pc = file.read_u32::<LittleEndian>().unwrap();
336        let initial_gp = file.read_u32::<LittleEndian>().unwrap();
337        let destination = file.read_u32::<LittleEndian>().unwrap();
338        let file_size = file.read_u32::<LittleEndian>().unwrap();
339        let _data_section_start = file.read_u32::<LittleEndian>().unwrap();
340        let _data_section_size = file.read_u32::<LittleEndian>().unwrap();
341        let _bss_section_start = file.read_u32::<LittleEndian>().unwrap();
342        let _bss_section_size = file.read_u32::<LittleEndian>().unwrap();
343        let mut initial_sp_fp = file.read_u32::<LittleEndian>().unwrap();
344        initial_sp_fp += file.read_u32::<LittleEndian>().unwrap();
345        // ascii marker and zero filled data
346        file.seek(SeekFrom::Start(0x800)).unwrap();
347
348        file.read_to_end(&mut data).unwrap();
349
350        assert!(data.len() == file_size as usize);
351
352        // put the data at the correct location in ram
353        self.dma_bus
354            .main_ram
355            .put_at_address(&data, destination & 0x1FFFFF);
356
357        (initial_pc, initial_gp, initial_sp_fp)
358    }
359
360    /// Since DMA is running using the CPU resources, we should run it and
361    /// treat the cycles consumed by it as if they were running from the CPU
362    pub fn clock_dma(&mut self) -> u32 {
363        self.dma.clock_dma(&mut self.dma_bus, &mut self.interrupts)
364    }
365
366    pub fn clock_components(&mut self, cpu_cycles: u32) {
367        let (dot_clocks, hblank_clock) = self.dma_bus.gpu.clock(&mut self.interrupts, cpu_cycles);
368
369        self.dma_bus.spu.clock(&mut self.interrupts, cpu_cycles);
370
371        // controller and mem card
372        self.controller_mem_card
373            .clock(&mut self.interrupts, cpu_cycles);
374
375        // cdrom (takes SPU to be able to send cdrom audio to the mixer)
376        self.dma_bus
377            .cdrom
378            .clock(&mut self.interrupts, &mut self.dma_bus.spu, cpu_cycles);
379
380        // timers
381        self.timers.clock_from_system(cpu_cycles);
382        if hblank_clock {
383            self.timers.clock_from_hblank();
384        }
385        self.timers.clock_from_gpu_dot(dot_clocks);
386        // interrupts for the timers
387        self.timers.handle_interrupts(&mut self.interrupts);
388    }
389
390    // implement the PSX memory map
391    // Note that `addr >= 0xFFFE0000` point to the cache control registers and isn't changed
392    fn map_address(&self, addr: u32) -> Result<u32> {
393        let region = addr >> 29;
394        const MASK_512M: u32 = 0x1FFFFFFF;
395
396        match region {
397            // KUSEG mirror of KSEG0/KSEG1
398            0 => Ok(addr & MASK_512M),
399            1..=3 => Err(String::from("Accessing bottom 1.5G of KUSEG")),
400            // KSEG0
401            4 => Ok(addr & MASK_512M),
402            // KSEG1
403            5 => {
404                if (0xBF800000..0xBF801000).contains(&addr) {
405                    Err(String::from("Cannot access scratchpad from KSEG1"))
406                } else {
407                    Ok(addr & MASK_512M)
408                }
409            }
410            // KSEG2
411            7 if addr >= 0xFFFE0000 => Ok(addr), // no change
412            6 | 7 => Err(String::from(
413                "KSEG2 has only the cache control registers at 0xFFFE0000",
414            )),
415            _ => unreachable!(),
416        }
417    }
418}
419
420impl BusLine for CpuBus {
421    fn read_u32(&mut self, addr: u32) -> Result<u32> {
422        assert!(addr % 4 == 0, "unalligned u32 read");
423        let addr = self.map_address(addr)?;
424
425        match addr {
426            // TODO: implement I-cache isolation properly
427            0x00000000..=0x007FFFFF => self.dma_bus.main_ram.read_u32(addr & 0x1FFFFF),
428            0x1FC00000..=0x1FC80000 => self.bios.read_u32(addr),
429            0x1F800000..=0x1F8003FF => self.scratchpad.read_u32(addr & 0x3FF),
430            0x1F801000..=0x1F801020 => self.mem_ctrl_1.read_u32(addr),
431            0x1F801044..=0x1F80104F => self.controller_mem_card.read_u32(addr & 0xF),
432            0x1F801060 => self.mem_ctrl_2.read_u32(addr),
433            0x1F801070..=0x1F801077 => self.interrupts.read_u32(addr & 0xF),
434            0x1F801080..=0x1F8010FC => self.dma.read_u32(addr & 0xFF),
435            0x1F801100..=0x1F80112F => self.timers.read_u32(addr & 0xFF),
436            0x1F801810..=0x1F801814 => self.dma_bus.gpu.read_u32(addr & 0xF),
437            0x1F801820..=0x1F801824 => self.dma_bus.mdec.read_u32(addr & 0xF),
438            0x1F801C00..=0x1F801FFC => self.dma_bus.spu.read_u32(addr & 0x3FF),
439            0x1F802000..=0x1F80208F => self.expansion_region_2.read_u32(addr & 0xFF),
440            0xFFFE0130 => self.cache_control.read_u32(addr),
441            _ => Err(format!("MainBus: u32 read from {:08X}", addr)),
442        }
443    }
444
445    fn write_u32(&mut self, addr: u32, data: u32) -> Result<()> {
446        assert!(addr % 4 == 0, "unalligned u32 write");
447        let addr = self.map_address(addr)?;
448
449        match addr {
450            0x00000000..=0x007FFFFF => self.dma_bus.main_ram.write_u32(addr & 0x1FFFFF, data),
451            0x1F800000..=0x1F8003FF => self.scratchpad.write_u32(addr & 0x3FF, data),
452            0x1F801000..=0x1F801020 => self.mem_ctrl_1.write_u32(addr, data),
453            0x1F801060 => self.mem_ctrl_2.write_u32(addr, data),
454            0x1F801070..=0x1F801077 => self.interrupts.write_u32(addr & 0xF, data),
455            0x1F801080..=0x1F8010FC => self.dma.write_u32(addr & 0xFF, data),
456            0x1F801100..=0x1F80112F => self.timers.write_u32(addr & 0xFF, data),
457            0x1F801810..=0x1F801814 => self.dma_bus.gpu.write_u32(addr & 0xF, data),
458            0x1F801820..=0x1F801824 => self.dma_bus.mdec.write_u32(addr & 0xF, data),
459            0x1F801C00..=0x1F801FFC => self.dma_bus.spu.write_u32(addr & 0x3FF, data),
460            0x1F802000..=0x1F80208F => self.expansion_region_2.write_u32(addr & 0xFF, data),
461            0xFFFE0130 => self.cache_control.write_u32(addr, data),
462            _ => Err(format!("MainBus: u32 write to {:08X}", addr)),
463        }
464    }
465
466    fn read_u16(&mut self, addr: u32) -> Result<u16> {
467        assert!(addr % 2 == 0, "unalligned u16 read");
468        let addr = self.map_address(addr)?;
469
470        match addr {
471            0x00000000..=0x007FFFFF => self.dma_bus.main_ram.read_u16(addr & 0x1FFFFF),
472            0x1F800000..=0x1F8003FF => self.scratchpad.read_u16(addr & 0x3FF),
473            0x1F801044..=0x1F80104F => self.controller_mem_card.read_u16(addr & 0xF),
474            0x1F801070..=0x1F801077 => self.interrupts.read_u16(addr & 0xF),
475            0x1F801100..=0x1F80112F => self.timers.read_u16(addr & 0xFF),
476            0x1F801C00..=0x1F801FFC => self.dma_bus.spu.read_u16(addr & 0x3FF),
477            0x1FC00000..=0x1FC80000 => self.bios.read_u16(addr),
478            0x1F802000..=0x1F80208F => self.expansion_region_2.read_u16(addr & 0xFF),
479            _ => Err(format!("u16 read from {:08X}", addr)),
480        }
481    }
482
483    fn write_u16(&mut self, addr: u32, data: u16) -> Result<()> {
484        assert!(addr % 2 == 0, "unalligned u16 write");
485        let addr = self.map_address(addr)?;
486
487        match addr {
488            0x00000000..=0x007FFFFF => self.dma_bus.main_ram.write_u16(addr & 0x1FFFFF, data),
489            0x1F800000..=0x1F8003FF => self.scratchpad.write_u16(addr & 0x3FF, data),
490            0x1F801048..=0x1F80104F => self.controller_mem_card.write_u16(addr & 0xF, data),
491            0x1F801070..=0x1F801077 => self.interrupts.write_u16(addr & 0xF, data),
492            0x1F801100..=0x1F80112F => self.timers.write_u16(addr & 0xFF, data),
493            0x1F801C00..=0x1F801FFC => self.dma_bus.spu.write_u16(addr & 0x3FF, data),
494            0x1F802000..=0x1F80208F => self.expansion_region_2.write_u16(addr & 0xFF, data),
495            _ => Err(format!("u16 write to {:08X}", addr)),
496        }
497    }
498    fn read_u8(&mut self, addr: u32) -> Result<u8> {
499        let addr = self.map_address(addr)?;
500
501        match addr {
502            0x00000000..=0x007FFFFF => self.dma_bus.main_ram.read_u8(addr & 0x1FFFFF),
503            0x1F800000..=0x1F8003FF => self.scratchpad.read_u8(addr & 0x3FF),
504            0x1F801040 => self.controller_mem_card.read_u8(addr & 0xF),
505            0x1F000000..=0x1F080000 => self.expansion_region_1.read_u8(addr & 0xFFFFF),
506            0x1F801080..=0x1F8010FF => self.dma.read_u8(addr & 0xFF),
507            0x1F801800..=0x1F801803 => self.dma_bus.cdrom.read_u8(addr & 3),
508            0x1F802000..=0x1F80208F => self.expansion_region_2.read_u8(addr & 0xFF),
509            0x1FC00000..=0x1FC80000 => self.bios.read_u8(addr),
510            _ => Err(format!("u8 read from {:08X}", addr)),
511        }
512    }
513
514    fn write_u8(&mut self, addr: u32, data: u8) -> Result<()> {
515        let addr = self.map_address(addr)?;
516
517        match addr {
518            0x00000000..=0x007FFFFF => self.dma_bus.main_ram.write_u8(addr & 0x1FFFFF, data),
519            0x1F800000..=0x1F8003FF => self.scratchpad.write_u8(addr & 0x3FF, data),
520            0x1F801040 => self.controller_mem_card.write_u8(addr & 0xF, data),
521            0x1F000000..=0x1F080000 => self.expansion_region_1.write_u8(addr & 0xFFFFF, data),
522            0x1F801080..=0x1F8010FF => self.dma.write_u8(addr & 0xFF, data),
523            0x1F801800..=0x1F801803 => self.dma_bus.cdrom.write_u8(addr & 3, data),
524            0x1F802000..=0x1F80208F => self.expansion_region_2.write_u8(addr & 0xFF, data),
525            _ => Err(format!("u8 write to {:08X}", addr)),
526        }
527    }
528}
529
530impl CpuBusProvider for CpuBus {
531    fn pending_interrupts(&self) -> bool {
532        self.interrupts.pending_interrupts()
533    }
534
535    fn should_run_dma(&self) -> bool {
536        self.dma.needs_to_run()
537    }
538}