lib.rsannotatedlib.rssource538 lines · 18.5 KB · raw

Emulation core for the Hitachi SH-2 CPU

Note that this core does not track timing. Callers can compute timing by assuming 1 cycle per instruction plus memory access delays, using the BusInterface implementation to record memory accesses.

7pub mod bus;
8mod cache;
9pub mod debug;
10mod disassemble;
11mod divu;
12mod dma;
13mod frt;
14mod instructions;
15mod registers;
16mod sci;
17mod wdt;
19use crate::bus::{AccessContext, BusInterface};
20use crate::cache::CpuCache;
21use crate::debug::{BusDebugExt, Sh2Debugger};
22use crate::divu::DivisionUnit;
23use crate::dma::DmaController;
24use crate::frt::FreeRunTimer;
25use crate::registers::{Sh2Registers, Sh7604Registers};
26use crate::sci::SerialInterface;
27use crate::wdt::WatchdogTimer;
28use bincode::{Decode, Encode};
29pub use disassemble::{
30    DisassembledInstruction, Displacement, MemoryAccess, MemoryAccessSize, ReadType,
31    disassemble_into,
32};
33pub use instructions::OpcodeTable;
34use jgenesis_common::debug::DebugMemoryView;
35use std::env;
36use std::fmt::Debug;
37
38pub use cache::CacheMode;
39pub use dma::{DmaAddressMode, DmaChannel, DmaChannelControl, DmaTransferUnit};
40
41const RESET_PC_VECTOR: u32 = 0x00000000;
42const RESET_SP_VECTOR: u32 = 0x00000004;
43
44const RESET_INTERRUPT_MASK: u8 = 15;
45const RESET_VBR: u32 = 0x00000000;
46
47const BASE_IRL_VECTOR_NUMBER: u32 = 64;

R15 is the hardware stack pointer

50const SP: usize = 15;

Only A0-28 are visible externally; A29-31 are handled internally

53const EXTERNAL_ADDRESS_MASK: u32 = 0x1FFFFFFF;
54const CACHE_LINE_MASK: u32 = EXTERNAL_ADDRESS_MASK & !0xF;
56#[derive(Debug, Clone, Encode, Decode)]
57pub struct Sh2 {
58    registers: Sh2Registers,
59    cache: CpuCache,
60    sh7604: Sh7604Registers,
61    dmac: DmaController,
62    free_run_timer: FreeRunTimer,
63    watchdog_timer: WatchdogTimer,
64    divu: DivisionUnit,
65    serial: SerialInterface,
66    reset_pending: bool,
67    data_ctx: AccessContext,
68    name: String,
69    trace_log_enabled: bool,
70}
71
72fn trace_log_enabled(name: &str) -> bool {
73    match env::var("SH2_LOG") {
74        Ok(log_name) => name == log_name,
75        Err(_) => true,
76    }
77}
78
79impl Sh2 {
80    #[must_use]
81    #[allow(clippy::missing_panics_doc)]
82    pub fn new(name: String) -> Self {
83        let trace_log_enabled = trace_log_enabled(&name);
84
85        Self {
86            registers: Sh2Registers::default(),
87            cache: CpuCache::new(),
88            sh7604: Sh7604Registers::new(),
89            dmac: DmaController::new(),
90            free_run_timer: FreeRunTimer::new(),
91            watchdog_timer: WatchdogTimer::new(),
92            divu: DivisionUnit::new(),
93            serial: SerialInterface::new(name.clone()),
94            reset_pending: true,
95            data_ctx: AccessContext::Data { pc: 0, opcode: 0 },
96            name,
97            trace_log_enabled,
98        }
99    }

Execute up to ticks instructions.

Will not execute any instructions if a reset is performed or an interrupt is handled.

104    #[inline]
105    pub fn execute<Bus: BusInterface>(&mut self, mut ticks: u64, bus: &mut Bus) {
106        if ticks == 0 {
107            return;
108        }
109
110        if bus.reset() {
111            self.reset_pending = true;
112            // TODO how long does a reset take?
113            bus.increment_cycle_counter(5);
114            return;
115        }
116
117        if self.reset_pending {
118            self.reset_pending = false;
119
120            // First 8 bytes of the address space contain the reset vector and the initial SP
121            // TODO use different vectors for manual reset vs. power-on reset? 32X doesn't depend on this
122            self.registers.pc = bus.read_longword(RESET_PC_VECTOR, AccessContext::InterruptVector);
123            self.registers.next_pc = self.registers.pc.wrapping_add(2);
124            self.registers.next_op_in_delay_slot = false;
125
126            self.registers.gpr[SP] =
127                bus.read_longword(RESET_SP_VECTOR, AccessContext::InterruptVector);
128
129            self.registers.sr.interrupt_mask = RESET_INTERRUPT_MASK;
130            self.registers.vbr = RESET_VBR;
131
132            self.cache.purge_all();
133
134            log::trace!(
135                "[{}] Reset SH-2; PC is {:08X} and SP is {:08X}",
136                self.name,
137                self.registers.pc,
138                self.registers.gpr[SP]
139            );
140
141            // TODO how long should this take?
142            bus.increment_cycle_counter(5);
143
144            return;
145        }
146
147        for _ in 0..ticks {
148            if !self.try_tick_dma(bus) {
149                break;
150            }
151        }
152
153        // Interrupts cannot trigger in a delay slot per the SH7604 hardware manual
154        // Before checking for interrupts, make sure the CPU is not in a delay slot
155        if self.registers.next_op_in_delay_slot {
156            self.execute_single_instruction(bus);
157            ticks -= 1;
158        }
159
160        debug_assert!(
161            !self.registers.next_op_in_delay_slot,
162            "SH-2 executed two simultaneous delay slot instructions, PC={:08X}",
163            self.registers.pc
164        );
165
166        let external_interrupt_level = bus.interrupt_level();
167        let internal_interrupt_level = self.sh7604.internal_interrupt.priority;
168        let interrupt_mask = self.registers.sr.interrupt_mask;
169
170        if external_interrupt_level > interrupt_mask
171            && external_interrupt_level >= internal_interrupt_level
172        {
173            let vector_number = BASE_IRL_VECTOR_NUMBER + u32::from(external_interrupt_level >> 1);
174            self.handle_exception(Some(external_interrupt_level), vector_number, bus);
175            return;
176        }
177
178        if internal_interrupt_level > interrupt_mask {
179            let vector_number: u32 = self.sh7604.internal_interrupt.vector_number.into();
180            self.handle_exception(Some(internal_interrupt_level), vector_number, bus);
181            return;
182        }
183
184        for _ in 0..ticks {
185            self.execute_single_instruction(bus);
186            if bus.should_stop_execution()
187                || self.registers.sr.interrupt_mask < bus.interrupt_level()
188                || self.registers.sr.interrupt_mask < self.sh7604.internal_interrupt.priority
189            {
190                return;
191            }
192        }
193    }
195    #[inline(always)]
196    fn execute_single_instruction<Bus: BusInterface>(&mut self, bus: &mut Bus) {
197        let pc = self.registers.pc;
198        let opcode = self.read_opcode(pc, bus);
199
200        bus.check_execute(pc, opcode, self);
201
202        self.registers.pc = self.registers.next_pc;
203        self.registers.next_pc = self.registers.pc.wrapping_add(2);
204        self.registers.next_op_in_delay_slot = false;
205
206        self.data_ctx = AccessContext::Data { pc, opcode };
207
208        if log::log_enabled!(log::Level::Trace) && self.trace_log_enabled {
209            let mut disassembled = DisassembledInstruction::new();
210            disassemble_into(pc, opcode, &mut disassembled);
211            log::trace!(
212                "[{}] Executing opcode {opcode:04X} at PC {pc:08X}: {}",
213                self.name,
214                disassembled.text
215            );
216            log::trace!("  Registers: {:08X?}", self.registers.gpr);
217            log::trace!(
218                "  GBR={:08X} VBR={:08X} PR={:08X}",
219                self.registers.gbr,
220                self.registers.vbr,
221                self.registers.pr
222            );
223            log::trace!("  SR={:?}", self.registers.sr);
224        }
225
226        let opcode_table = Bus::opcode_table();
227        opcode_table.decode(opcode)(self, opcode, bus);
228        bus.increment_cycle_counter(1);
229    }

Advance internal peripherals by system_cycles, specifically the watchdog timer (WDT) and the serial interface (SCI). Also updates internal interrupt state.

233    #[inline]
234    pub fn tick_peripherals<Bus: BusInterface>(&mut self, system_cycles: u64, bus: &mut Bus) {
235        self.watchdog_timer.tick(system_cycles);
236        self.serial.process(system_cycles, bus);
237        self.update_internal_interrupt_level();
238    }
240    fn read_byte<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u8 {
241        bus.check_read_byte(address, self);
242
243        match address >> 29 {
244            0 => self.cached_read_byte(address, bus),
245            1 => bus.apply_read_byte(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self),
246            2 => {
247                self.cache.associative_purge(address);
248                0
249            }
250            3..=5 => {
251                log::warn!(
252                    "SH-2 {:?} invalid byte read: {address:08X}, ctx: {}",
253                    self.name,
254                    self.data_ctx
255                );
256                0
257            }
258            6 => self.cache.read_data_array_u8(address),
259            7 => self.read_internal_register_byte(address, bus),
260            _ => unreachable!("u32 >> 29 is always 0-7"),
261        }
262    }
263
264    fn cached_read_byte<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u8 {
265        if let Some(value) = self.cache.read_u8(address) {
266            return value;
267        }
268
269        if self.cache.should_replace_data() {
270            let cache_line =
271                bus.apply_read_cache_line(address & CACHE_LINE_MASK, self.data_ctx, self);
272            let longword = self.cache.replace(address, cache_line);
273            longword.to_be_bytes()[(address & 3) as usize]
274        } else {
275            bus.apply_read_byte(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self)
276        }
277    }
278
279    fn read_word<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u16 {
280        self.read_word_generic::<false, _>(address, bus)
281    }
282
283    #[inline(always)]
284    fn read_opcode<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u16 {
285        self.read_word_generic::<true, _>(address, bus)
286    }
287
288    #[inline(always)]
289    fn read_word_generic<const INSTRUCTION: bool, Bus: BusInterface>(
290        &mut self,
291        address: u32,
292        bus: &mut Bus,
293    ) -> u16 {
294        if !INSTRUCTION {
295            bus.check_read_word(address, self);
296        }
297
298        match address >> 29 {
299            0 => self.cached_read_word::<INSTRUCTION, _>(address, bus),
300            1 => {
301                let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx };
302                bus.apply_read_word(address & EXTERNAL_ADDRESS_MASK, ctx, self)
303            }
304            2 => {
305                self.cache.associative_purge(address);
306                0
307            }
308            3..=5 => {
309                let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx };
310                log::warn!("SH-2 {:?} invalid word read: {address:08X}, ctx: {ctx}", self.name);
311                0
312            }
313            6 => self.cache.read_data_array_u16(address),
314            7 => self.read_internal_register_word(address),
315            _ => unreachable!("u32 >> 29 is always 0-7"),
316        }
317    }
318
319    #[inline(always)]
320    fn cached_read_word<const INSTRUCTION: bool, Bus: BusInterface>(
321        &mut self,
322        address: u32,
323        bus: &mut Bus,
324    ) -> u16 {
325        if let Some(value) = self.cache.read_u16(address) {
326            return value;
327        }
328
329        if (INSTRUCTION && self.cache.should_replace_instruction())
330            || (!INSTRUCTION && self.cache.should_replace_data())
331        {
332            let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx };
333            let cache_line = bus.apply_read_cache_line(address & CACHE_LINE_MASK, ctx, self);
334            let longword = self.cache.replace(address, cache_line);
335            (longword >> (16 * (((address >> 1) & 1) ^ 1))) as u16
336        } else {
337            let ctx = if INSTRUCTION { AccessContext::Fetch } else { self.data_ctx };
338            bus.apply_read_word(address & EXTERNAL_ADDRESS_MASK, ctx, self)
339        }
340    }
341
342    fn read_longword<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u32 {
343        bus.check_read_longword(address, self);
344
345        match address >> 29 {
346            0 => self.cached_read_longword(address, bus),
347            1 => bus.apply_read_longword(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self),
348            2 => {
349                // FIFA Soccer 96 reads from associative purge addresses and doesn't use the values read
350                // Seems like it expects reads to purge cache lines in addition to writes?
351                self.cache.associative_purge(address);
352                0
353            }
354            3 => self.cache.read_address_array(address),
355            4 | 5 => {
356                log::warn!(
357                    "SH-2 {:?} invalid longword read: {address:08X}, ctx: {}",
358                    self.name,
359                    self.data_ctx
360                );
361                0
362            }
363            6 => self.cache.read_data_array_u32(address),
364            7 => self.read_internal_register_longword(address),
365            _ => unreachable!("u32 >> 29 is always 0-7"),
366        }
367    }
368
369    fn cached_read_longword<Bus: BusInterface>(&mut self, address: u32, bus: &mut Bus) -> u32 {
370        if let Some(value) = self.cache.read_u32(address) {
371            return value;
372        }
373
374        if self.cache.should_replace_data() {
375            let cache_line =
376                bus.apply_read_cache_line(address & CACHE_LINE_MASK, self.data_ctx, self);
377            self.cache.replace(address, cache_line)
378        } else {
379            bus.apply_read_longword(address & EXTERNAL_ADDRESS_MASK, self.data_ctx, self)
380        }
381    }
382
383    fn write_byte<Bus: BusInterface>(&mut self, address: u32, value: u8, bus: &mut Bus) {
384        bus.check_write_byte(address, value, self);
385
386        match address >> 29 {
387            0 => {
388                bus.apply_write_byte(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self);
389                self.cache.write_through_u8(address, value);
390            }
391            1 => bus.apply_write_byte(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self),
392            2 => self.cache.associative_purge(address),
393            3..=5 => {
394                log::warn!(
395                    "SH-2 {:?} invalid byte write: {address:08X} {value:02X}, ctx: {}",
396                    self.name,
397                    self.data_ctx
398                );
399            }
400            6 => self.cache.write_data_array_u8(address, value),
401            7 => self.write_internal_register_byte(address, value, bus),
402            _ => unreachable!("u32 >> 29 is always 0-7"),
403        }
404    }
405
406    fn write_word<Bus: BusInterface>(&mut self, address: u32, value: u16, bus: &mut Bus) {
407        bus.check_write_word(address, value, self);
408
409        match address >> 29 {
410            0 => {
411                bus.apply_write_word(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self);
412                self.cache.write_through_u16(address, value);
413            }
414            1 => bus.apply_write_word(address & EXTERNAL_ADDRESS_MASK, value, self.data_ctx, self),
415            2 => self.cache.associative_purge(address),
416            3..=5 => {
417                log::warn!(
418                    "SH-2 {:?} invalid word write: {address:08X} {value:04X}, ctx: {}",
419                    self.name,
420                    self.data_ctx
421                );
422            }
423            6 => self.cache.write_data_array_u16(address, value),
424            7 => self.write_internal_register_word(address, value),
425            _ => unreachable!("u32 >> 29 is always 0-7"),
426        }
427    }
428
429    #[allow(clippy::match_same_arms)]
430    fn write_longword<Bus: BusInterface>(&mut self, address: u32, value: u32, bus: &mut Bus) {
431        bus.check_write_longword(address, value, self);
432
433        match address >> 29 {
434            0 => {
435                bus.apply_write_longword(
436                    address & EXTERNAL_ADDRESS_MASK,
437                    value,
438                    self.data_ctx,
439                    self,
440                );
441                self.cache.write_through_u32(address, value);
442            }
443            1 => bus.apply_write_longword(
444                address & EXTERNAL_ADDRESS_MASK,
445                value,
446                self.data_ctx,
447                self,
448            ),
449            2 => self.cache.associative_purge(address),
450            3 => self.cache.write_address_array(address, value),
451            4 | 5 => {
452                log::warn!(
453                    "SH-2 {:?} invalid longword write: {address:08X} {value:08X}, ctx: {}",
454                    self.name,
455                    self.data_ctx
456                );
457            }
458            6 => self.cache.write_data_array_u32(address, value),
459            7 => self.write_internal_register_longword(address, value),
460            _ => unreachable!("u32 >> 29 is always 0-7"),
461        }
462    }
463
464    fn handle_exception<Bus: BusInterface>(
465        &mut self,
466        interrupt_level: Option<u8>,
467        vector_number: u32,
468        bus: &mut Bus,
469    ) {
470        if let Some(mut debug_view) = bus.debug_view()
471            && let Some(interrupt_level) = interrupt_level
472        {
473            debug_view.check_interrupt(interrupt_level, self);
474        }
475
476        let mut sp = self.registers.gpr[SP].wrapping_sub(4);
477        self.write_longword(sp, self.registers.sr.into(), bus);
478
479        sp = sp.wrapping_sub(4);
480        self.write_longword(sp, self.registers.pc, bus);
481
482        self.registers.gpr[SP] = sp;
483        if let Some(interrupt_level) = interrupt_level {
484            self.registers.sr.interrupt_mask = interrupt_level;
485        }
486
487        let vector_addr = self.registers.vbr.wrapping_add(vector_number << 2);
488        self.registers.pc = self.read_longword(vector_addr, bus);
489        self.registers.next_pc = self.registers.pc.wrapping_add(2);
490        self.registers.next_op_in_delay_slot = false;
491
492        // Interrupt handling takes 10 cycles, plus memory access time, plus time to flush the pipeline
493        // Memory access time is already accounted for, so arbitrarily say it will take 3 cycles to flush
494        bus.increment_cycle_counter(13);
495
496        log::debug!(
497            "[{}] Handled interrupt of level {interrupt_level:?} with vector number {vector_number}, jumped to {:08X}",
498            self.name,
499            self.registers.pc
500        );
501    }
502
503    fn update_internal_interrupt_level(&mut self) {
504        self.sh7604.update_interrupt_level(&self.dmac, &self.watchdog_timer, &self.serial);
505    }
506
507    #[inline]
508    #[must_use]
509    pub fn pc(&self) -> u32 {
510        self.registers.pc
511    }
512
513    pub fn set_pc(&mut self, pc: u32) {
514        self.registers.pc = pc;
515        self.registers.next_pc = pc.wrapping_add(2);
516    }
517
518    #[inline]
519    #[must_use]
520    pub fn registers(&self) -> &Sh2Registers {
521        &self.registers
522    }
523
524    #[must_use]
525    pub fn peek_cache(&self, address: u32) -> Option<u16> {
526        self.cache.peek(address)
527    }
528
529    #[must_use]
530    pub fn peek_data_array(&self, address: u32) -> u16 {
531        self.cache.peek_data_array(address)
532    }
533
534    #[must_use]
535    pub fn debug_cache_view(&mut self) -> impl DebugMemoryView {
536        self.cache.debug_view()
537    }
538}