memory.rsannotatedmemory.rssource509 lines · 15.8 KB · raw
1use crate::api::PceEmulatorConfig;
2use bincode::{Decode, Encode};
3use crc::Crc;
4use huc6280_emu::bus::{ClockSpeed, InterruptLines};
5use jgenesis_common::boxedarray::BoxedByteArray;
6use jgenesis_common::num::GetBit;
7use jgenesis_proc_macros::{FakeDecode, FakeEncode, PartialClone};
8use std::ops::Deref;
9use std::{cmp, iter, mem};
10
11const WORKING_RAM_LEN: usize = 8 * 1024;
12
13const POPULOUS_SRAM_LEN: usize = 32 * 1024;

Timer ticks every 1024 cycles at ~7.16 MHz regardless of current CPU clock speed

16const TIMER_PRESCALER_DIVIDER: u64 = 3 * 1024;
18#[derive(Debug, Clone, FakeEncode, FakeDecode)]
19pub struct Rom(pub Box<[u8]>);
20
21impl Default for Rom {
22    fn default() -> Self {
23        Self(vec![].into_boxed_slice())
24    }
25}
26
27impl Deref for Rom {
28    type Target = Box<[u8]>;
29
30    fn deref(&self) -> &Self::Target {
31        &self.0
32    }
33}
34
35#[derive(Debug, Clone, Encode, Decode)]
36enum Mapper {
37    // Standard linear ROM mapping in all banks
38    None,
39    // Standard linear ROM mapping in banks $00-$3F, 32KB of SRAM mapped to $40-$43
40    Populous { sram: BoxedByteArray<POPULOUS_SRAM_LEN>, sram_dirty: bool },
41    // First 512KB of ROM in banks $00-$3F, mappable 512KB ROM bank in banks $40-$7F
42    StreetFighter2 { rom_bank: u32 },
43}
44
45impl Mapper {
46    fn guess_from_rom(rom: &[u8], initial_sram: Option<Vec<u8>>) -> Self {
47        const CRC: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
48
49        let checksum = CRC.checksum(rom);
50
51        // TODO is there a better way to do this than checksum matching? PCE games don't seem to
52        // have anything resembling a cartridge header
53        match checksum {
54            // Populous (Japan) (En)
55            0xDB5F97B3 => {
56                log::info!("Enabling Populous SRAM mapper (ROM checksum {checksum:08X})");
57
58                let mut sram = BoxedByteArray::new();
59                if let Some(initial_sram) = initial_sram
60                    && initial_sram.len() >= POPULOUS_SRAM_LEN
61                {
62                    sram.copy_from_slice(&initial_sram[..POPULOUS_SRAM_LEN]);
63                }
64
65                Self::Populous { sram, sram_dirty: false }
66            }
67            // Street Fighter II' - Champion Edition (Japan)
68            0x33DEB700 => {
69                log::info!(
70                    "Enabling Street Fighter II bank-switching mapper (ROM checksum {checksum:08X})"
71                );
72                Self::StreetFighter2 { rom_bank: 1 }
73            }
74            _ => {
75                log::info!("Using standard mapper");
76                Self::None
77            }
78        }
79    }
80
81    fn read(&self, address: u32, rom: &[u8]) -> u8 {
82        debug_assert!(address <= 0x0FFFFF);
83
84        match self {
85            Self::None => read_rom_safely(rom, address),
86            Self::Populous { sram, .. } => match address >> 13 {
87                0x40..=0x43 => sram[(address & 0x7FFF) as usize],
88                _ => read_rom_safely(rom, address),
89            },
90            &Self::StreetFighter2 { rom_bank } => match address {
91                0x000000..=0x07FFFF => read_rom_safely(rom, address),
92                0x080000..=0x0FFFFF => {
93                    let banked_addr = (rom_bank << 19) | (address & 0x7FFFF);
94                    read_rom_safely(rom, banked_addr)
95                }
96                _ => panic!("Invalid ROM address {address:06X}"),
97            },
98        }
99    }
100
101    fn write(&mut self, address: u32, value: u8) {
102        match self {
103            Self::None => {}
104            Self::Populous { sram, sram_dirty } => {
105                let bank = address >> 13;
106                if (0x40..=0x43).contains(&bank) {
107                    sram[(address & 0x7FFF) as usize] = value;
108                    *sram_dirty = true;
109                }
110            }
111            Self::StreetFighter2 { rom_bank } => {
112                // Writing to $1FF0-$1FF3 changes the ROM bank based on the address written to
113                // Value does not matter
114                if (0x001FF0..=0x001FF3).contains(&address) {
115                    *rom_bank = (address & 3) + 1;
116                }
117            }
118        }
119    }
120}
121
122#[inline(always)]
123fn read_rom_safely(rom: &[u8], address: u32) -> u8 {
124    rom[(address as usize) & (rom.len() - 1)]
125}
126
127#[derive(Debug, Clone, PartialClone, Encode, Decode)]
128pub struct HuCard {
129    #[partial_clone(default)]
130    rom: Rom,
131    mapper: Mapper,
132}
133
134impl HuCard {
135    pub fn new(mut rom: Vec<u8>, initial_sram: Option<Vec<u8>>) -> Self {
136        rom = mirror_hucard_rom(rom);
137
138        let mapper = Mapper::guess_from_rom(&rom, initial_sram);
139
140        Self { rom: Rom(rom.into_boxed_slice()), mapper }
141    }
142
143    pub fn read(&self, address: u32) -> u8 {
144        self.mapper.read(address, &self.rom)
145    }
146
147    pub fn write(&mut self, address: u32, value: u8) {
148        self.mapper.write(address, value);
149    }
150
151    pub fn clone_rom(&self) -> Vec<u8> {
152        self.rom.0.to_vec()
153    }
154
155    pub fn take_rom_from(&mut self, other: &mut Self) {
156        self.rom.0 = mem::take(&mut other.rom.0);
157    }
158
159    pub fn sram(&self) -> Option<&[u8]> {
160        match &self.mapper {
161            Mapper::Populous { sram, .. } => Some(sram.as_slice()),
162            _ => None,
163        }
164    }
165
166    pub fn is_sram_dirty(&self) -> bool {
167        match self.mapper {
168            Mapper::Populous { sram_dirty, .. } => sram_dirty,
169            _ => false,
170        }
171    }
172
173    pub fn clear_sram_dirty(&mut self) {
174        if let Mapper::Populous { sram_dirty, .. } = &mut self.mapper {
175            *sram_dirty = false;
176        }
177    }
178}
179
180fn mirror_hucard_rom(mut rom: Vec<u8>) -> Vec<u8> {
181    if rom.is_empty() {
182        // Nothing really reasonable to do here; just make the entire cartridge read 0xFF
183        rom.extend(iter::repeat_n(0xFF, 256 * 1024));
184    }
185
186    let mut new_rom = if rom.len() == 384 * 1024 {
187        // 384KB HuCards contain two ROM chips, a 256KB chip and a 128KB chip, mapped like so:
188        //   $000000-$07FFFF (banks $00-$3F): First 256KB of ROM, mirrored 2x
189        //   $080000-$0FFFFF (banks $40-$7F): Last 128KB of ROM, mirrored 4x
190        let mut new_rom = Vec::with_capacity(1024 * 1024);
191
192        for _ in 0..2 {
193            new_rom.extend(&rom[..256 * 1024]);
194        }
195        new_rom.extend(&rom[256 * 1024..]);
196
197        new_rom
198    } else if rom.len() == 512 * 1024 {
199        // 512KB HuCards can apparently be one of two mappings.
200        // Mapping A (2x 256KB chips):
201        //   $000000-$07FFFF (banks $00-$3F): First 256KB of ROM, mirrored 2x
202        //   $080000-$0FFFFF (banks $40-$7F): Last 256KB of ROM, mirrored 2x
203        // Mapping B (1x 512KB chip):
204        //   $000000-$0FFFFF (banks $00-$7F): Full 512KB of ROM, mirrored 2x
205        // It's virtually impossible to detect which mapping a game expects, so for highest
206        // compatibility, mirror the last 256KB of ROM 3x (inspired by what Mednafen does).
207        // Explicitly:
208        //   $00-$1F: First 256KB
209        //   $20-$3F: Second 256KB (important for games with 1x 512KB chip)
210        //   $40-$5F: Second 256KB (important for games with 2x 256KB chips)
211        //   $60-$7F: Second 256KB (probably never used?)
212        if rom.capacity() < 1024 * 1024 {
213            rom.reserve(1024 * 1024 - rom.capacity());
214        }
215
216        for i in 256 * 1024..512 * 1024 {
217            rom.push(rom[i]);
218        }
219
220        rom
221    } else {
222        // For other sizes (e.g. 768KB or 1MB), normal mirroring up to the next power of two works
223        rom
224    };
225
226    jgenesis_common::rom::mirror_to_next_power_of_two(&mut new_rom);
227
228    new_rom
229}
230
231#[derive(Debug, Clone, Encode, Decode)]
232struct Timer {
233    counter: u8,
234    reload: u8,
235    prescaler: u64,
236    enabled: bool,
237    cycles: u64,
238    next_overflow_cycles: u64,
239    just_overflowed: bool,
240}
241
242impl Timer {
243    fn new() -> Self {
244        Self {
245            reload: 0,
246            counter: 0,
247            prescaler: TIMER_PRESCALER_DIVIDER,
248            enabled: false,
249            cycles: 0,
250            next_overflow_cycles: u64::MAX,
251            just_overflowed: false,
252        }
253    }
254
255    fn step_to(&mut self, cycles: u64, tiq_pending: &mut bool) {
256        if cycles < self.next_overflow_cycles {
257            return;
258        }
259
260        self.force_step_to(cycles, tiq_pending);
261    }
262
263    fn force_step_to(&mut self, cycles: u64, tiq_pending: &mut bool) {
264        if !self.enabled {
265            self.cycles = cycles;
266            return;
267        }
268
269        let mut elapsed_cycles = cycles.saturating_sub(self.cycles);
270        self.cycles = cycles;
271
272        while elapsed_cycles != 0 {
273            if self.just_overflowed {
274                self.counter = self.reload;
275                self.just_overflowed = false;
276            }
277
278            let timer_elapsed = cmp::min(elapsed_cycles, self.prescaler);
279            self.prescaler -= timer_elapsed;
280            elapsed_cycles -= timer_elapsed;
281
282            if self.prescaler == 0 {
283                self.prescaler = TIMER_PRESCALER_DIVIDER;
284
285                if self.counter == 0 {
286                    *tiq_pending = true;
287                    self.just_overflowed = true;
288
289                    // On overflow, allow the counter to read 0x7F for a single cycle afterwards.
290                    // This fixes Battle Royale failing to boot; it depends on being able to eventually
291                    // read a non-zero timer counter value while the timer reload is zero
292                }
293                self.counter = self.counter.wrapping_sub(1) & 0x7F;
294            }
295        }
296
297        let effective_counter = if self.just_overflowed { self.reload } else { self.counter };
298        self.next_overflow_cycles =
299            cycles + self.prescaler + u64::from(effective_counter) * TIMER_PRESCALER_DIVIDER;
300    }
301
302    // $1FEC00: Timer reload value
303    fn write_reload(&mut self, value: u8) {
304        self.reload = value & 0x7F;
305
306        log::trace!("Timer reload: {}", self.reload);
307    }
308
309    // $1FEC01: Timer enabled
310    fn write_enabled(&mut self, value: u8, cycles: u64) {
311        let prev_enabled = self.enabled;
312        self.enabled = value.bit(0);
313
314        if !prev_enabled && self.enabled {
315            self.counter = self.reload;
316            self.prescaler = TIMER_PRESCALER_DIVIDER;
317
318            self.cycles = cycles;
319            self.next_overflow_cycles =
320                cycles + TIMER_PRESCALER_DIVIDER * u64::from(self.counter + 1);
321            self.just_overflowed = false;
322
323            log::trace!(
324                "Timer newly enabled at cycles {cycles}; next overflow at {}",
325                self.next_overflow_cycles
326            );
327        }
328
329        if !self.enabled {
330            // Timer will never overflow until it's enabled again
331            self.next_overflow_cycles = u64::MAX;
332        }
333
334        log::trace!("Timer enabled: {} (cycles {cycles})", self.enabled);
335    }
336}
337
338#[derive(Debug, Clone, Encode, Decode)]
339pub struct CpuRegisters {
340    clock_speed: ClockSpeed,
341    tiq_disabled: bool,
342    tiq_pending: bool,
343    irq1_disabled: bool,
344    irq1_pending: bool,
345    irq2_disabled: bool,
346    irq2_pending: bool,
347    timer: Timer,
348    io_buffer: u8,
349}
350
351impl CpuRegisters {
352    fn new() -> Self {
353        Self {
354            clock_speed: ClockSpeed::default(),
355            tiq_disabled: false,
356            tiq_pending: false,
357            irq1_disabled: false,
358            irq1_pending: false,
359            irq2_disabled: false,
360            irq2_pending: false,
361            timer: Timer::new(),
362            io_buffer: 0xFF,
363        }
364    }
365
366    pub fn io_buffer(&self) -> u8 {
367        self.io_buffer
368    }
369
370    pub fn update_io_buffer(&mut self, value: u8, mask: u8) -> u8 {
371        self.io_buffer = (value & mask) | (self.io_buffer & !mask);
372        self.io_buffer
373    }
374
375    pub fn irq1_pending_mut(&mut self) -> &mut bool {
376        &mut self.irq1_pending
377    }
378
379    pub fn step_timer_to(&mut self, cycles: u64) {
380        self.timer.step_to(cycles, &mut self.tiq_pending);
381    }
382
383    // $1FF400-$1FF403: Interrupt registers
384    pub fn read_interrupt_register(&mut self, address: u32) -> u8 {
385        match address & 3 {
386            0 | 1 => {} // Unused
387            2 => {
388                self.io_buffer = (self.io_buffer & 0xF8)
389                    | (u8::from(self.tiq_disabled) << 2)
390                    | (u8::from(self.irq1_disabled) << 1)
391                    | u8::from(self.irq2_disabled);
392            }
393            3 => {
394                self.io_buffer = (self.io_buffer & 0xF8)
395                    | (u8::from(self.tiq_pending) << 2)
396                    | (u8::from(self.irq1_pending) << 1)
397                    | u8::from(self.irq2_pending);
398            }
399            _ => unreachable!("value & 3 is always <= 3"),
400        }
401
402        self.io_buffer
403    }
404
405    // $1FF400-$1FF403: Interrupt registers
406    pub fn write_interrupt_register(&mut self, address: u32, value: u8) {
407        log::trace!("Interrupt register write: {address:06X} {value:02X}");
408
409        self.io_buffer = value;
410
411        match address & 3 {
412            0 | 1 => {} // Unused
413            2 => {
414                self.tiq_disabled = value.bit(2);
415                self.irq1_disabled = value.bit(1);
416                self.irq2_disabled = value.bit(0);
417
418                log::trace!("TIQ enabled: {}", !self.tiq_disabled);
419                log::trace!("IRQ1 enabled: {}", !self.irq1_disabled);
420                log::trace!("IRQ2 enabled: {}", !self.irq2_disabled);
421            }
422            3 => {
423                // All writes acknowledge the timer interrupt
424                self.tiq_pending = false;
425
426                log::trace!("TIQ acknowledged");
427            }
428            _ => unreachable!("value & 3 is always <= 3"),
429        }
430    }
431
432    // $1FEC00-$1FEC01: Timer registers
433    pub fn read_timer_register(&mut self, cycles: u64) -> u8 {
434        self.timer.force_step_to(cycles, &mut self.tiq_pending);
435
436        self.io_buffer = (self.io_buffer & !0x7F) | (self.timer.counter & 0x7F);
437        self.io_buffer
438    }
439
440    // $1FEC00-$1FEC01: Timer registers
441    pub fn write_timer_register(&mut self, address: u32, value: u8, cycles: u64) {
442        self.timer.force_step_to(cycles, &mut self.tiq_pending);
443
444        self.io_buffer = value;
445
446        match address & 1 {
447            0 => self.timer.write_reload(value),
448            1 => self.timer.write_enabled(value, cycles),
449            _ => unreachable!("value & 1 is always <= 1"),
450        }
451    }
452
453    pub fn interrupt_lines(&self) -> InterruptLines {
454        InterruptLines {
455            irq1: self.irq1_pending && !self.irq1_disabled,
456            irq2: self.irq2_pending && !self.irq2_disabled,
457            tiq: self.tiq_pending && !self.tiq_disabled,
458        }
459    }
460}
461
462#[derive(Debug, Clone, Encode, Decode)]
463pub struct Memory {
464    working_ram: BoxedByteArray<WORKING_RAM_LEN>,
465    cpu_registers: CpuRegisters,
466    cpu_fast_clock_divider: u64,
467}
468
469impl Memory {
470    pub fn new(config: &PceEmulatorConfig) -> Self {
471        Self {
472            working_ram: BoxedByteArray::new_random(),
473            cpu_registers: CpuRegisters::new(),
474            cpu_fast_clock_divider: config.clamped_cpu_fast_divider(),
475        }
476    }
477
478    pub fn read_working_ram(&self, address: u32) -> u8 {
479        self.working_ram[(address as usize) & (WORKING_RAM_LEN - 1)]
480    }
481
482    pub fn write_working_ram(&mut self, address: u32, value: u8) {
483        self.working_ram[(address as usize) & (WORKING_RAM_LEN - 1)] = value;
484    }
485
486    pub fn cpu_clock_divider(&self) -> u64 {
487        match self.cpu_registers.clock_speed {
488            ClockSpeed::Low => 12,                           // ~1.79 MHz
489            ClockSpeed::High => self.cpu_fast_clock_divider, // ~7.16 MHz when not overclocking
490        }
491    }
492
493    pub fn set_clock_speed(&mut self, speed: ClockSpeed) {
494        log::trace!("Clock speed set to {speed:?}");
495        self.cpu_registers.clock_speed = speed;
496    }
497
498    pub fn cpu_registers(&mut self) -> &mut CpuRegisters {
499        &mut self.cpu_registers
500    }
501
502    pub fn interrupt_lines(&self) -> InterruptLines {
503        self.cpu_registers.interrupt_lines()
504    }
505
506    pub fn reload_config(&mut self, config: &PceEmulatorConfig) {
507        self.cpu_fast_clock_divider = config.clamped_cpu_fast_divider();
508    }
509}