Skip to content

Commit

Permalink
Don't exit guest context during idle loops
Browse files Browse the repository at this point in the history
  • Loading branch information
Grarak committed Feb 1, 2025
1 parent 380f488 commit 306aed9
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 28 deletions.
13 changes: 7 additions & 6 deletions src/cartridge_io.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::cartridge_metadata::get_cartridge_metadata;
use crate::logging::debug_println;
use crate::mmap::PAGE_SIZE;
use crate::utils;
use crate::utils::{rgb5_to_rgb8, HeapMemU8, NoHashMap};
use static_assertions::const_assert_eq;
Expand Down Expand Up @@ -78,13 +77,15 @@ pub const HEADER_IN_RAM_SIZE: usize = 0x170;
const_assert_eq!(HEADER_SIZE, HEADER_IN_RAM_SIZE + 0x90);

const SAVE_SIZES: [u32; 9] = [0x000200, 0x002000, 0x008000, 0x010000, 0x020000, 0x040000, 0x080000, 0x100000, 0x800000];
const CARTRIDGE_PAGE_SIZE: usize = 4096;
const MAX_CARTRIDGE_CACHE: usize = 512 * 1024;

pub struct CartridgeIo {
file: File,
pub file_name: String,
pub file_size: u32,
pub header: CartridgeHeader,
content_pages: UnsafeCell<NoHashMap<u32, HeapMemU8<{ PAGE_SIZE }>>>,
content_pages: UnsafeCell<NoHashMap<u32, HeapMemU8<{ CARTRIDGE_PAGE_SIZE }>>>,
save_file_path: PathBuf,
pub save_file_size: u32,
save_buf: Mutex<(Vec<u8>, bool)>,
Expand Down Expand Up @@ -136,13 +137,13 @@ impl CartridgeIo {
})
}

fn get_page(&self, page_addr: u32) -> io::Result<*const [u8; PAGE_SIZE]> {
debug_assert_eq!(page_addr & (PAGE_SIZE as u32 - 1), 0);
fn get_page(&self, page_addr: u32) -> io::Result<*const [u8; CARTRIDGE_PAGE_SIZE]> {
debug_assert_eq!(page_addr & (CARTRIDGE_PAGE_SIZE as u32 - 1), 0);
let pages = unsafe { self.content_pages.get().as_mut_unchecked() };
match pages.get(&page_addr) {
None => {
// exceeds 0.5MB
if pages.len() >= 128 {
if pages.len() >= MAX_CARTRIDGE_CACHE / CARTRIDGE_PAGE_SIZE {
debug_println!("clear cartridge pages");
pages.clear();
}
Expand All @@ -162,7 +163,7 @@ impl CartridgeIo {
while remaining > 0 {
let slice_start = slice.len() - remaining;

let page_addr = (offset + slice_start as u32) & !(PAGE_SIZE as u32 - 1);
let page_addr = (offset + slice_start as u32) & !(CARTRIDGE_PAGE_SIZE as u32 - 1);
let page_offset = offset + slice_start as u32 - page_addr;
let page = self.get_page(page_addr)?;
let page = unsafe { page.as_ref_unchecked() };
Expand Down
32 changes: 26 additions & 6 deletions src/jit/emitter/emit_branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ use crate::core::CpuType;
use crate::core::CpuType::{ARM7, ARM9};
use crate::jit::assembler::block_asm::BlockAsm;
use crate::jit::assembler::BlockReg;
use crate::jit::inst_branch_handler::handle_idle_loop;
use crate::jit::inst_info::InstInfo;
use crate::jit::jit_asm::{align_guest_pc, JitAsm, JitRuntimeData};
use crate::jit::jit_asm_common_funs::JitAsmCommonFuns;
use crate::jit::op::Op;
use crate::jit::reg::{reg_reserve, Reg, RegReserve};
use crate::jit::Cond;
use crate::DEBUG_LOG;
use crate::{DEBUG_LOG, IS_DEBUG};
use std::ptr;

pub enum JitBranchInfo {
Idle,
Idle(usize),
Local(usize),
None,
}
Expand Down Expand Up @@ -50,7 +51,7 @@ impl<const CPU: CpuType> JitAsm<'_, CPU> {
if diff as usize <= branch_index {
let jump_to_index = branch_index - diff as usize;
if Self::is_idle_loop(&insts[jump_to_index..branch_index + 1]) {
return JitBranchInfo::Idle;
return JitBranchInfo::Idle(jump_to_index);
}
}
}
Expand Down Expand Up @@ -131,14 +132,33 @@ impl<const CPU: CpuType> JitAsm<'_, CPU> {
block_asm.free_reg(target_pre_cycle_count_sum_reg);
block_asm.free_reg(total_cycles_reg);
}
JitBranchInfo::Idle => {
JitBranchInfo::Idle(jump_to_index) => {
block_asm.mov(Reg::PC, target_pc);
block_asm.save_context();
if DEBUG_LOG {
block_asm.call2(Self::debug_idle_loop as *const (), self.jit_buf.current_pc, target_pc);
}
self.emit_branch_out_metadata_with_idle_loop(block_asm);
block_asm.epilogue();
match CPU {
ARM9 => {
let target_pre_cycle_count_sum = self.jit_buf.insts_cycle_counts[jump_to_index] - self.jit_buf.insts[jump_to_index].cycle as u16;
let func = if self.emu.settings.arm7_hle() {
handle_idle_loop::<true> as *const ()
} else {
handle_idle_loop::<false> as *const ()
};
if IS_DEBUG {
block_asm.call3(func, self as *mut _ as u32, target_pre_cycle_count_sum as u32, self.jit_buf.current_pc);
} else {
block_asm.call2(func, self as *mut _ as u32, target_pre_cycle_count_sum as u32);
}
block_asm.restore_reg(Reg::CPSR);
block_asm.guest_branch(Cond::AL, target_pc & !1);
}
ARM7 => {
self.emit_branch_out_metadata_with_idle_loop(block_asm);
block_asm.epilogue();
}
}
}
JitBranchInfo::None => self.emit_branch_external_label(block_asm, target_pc, false, false),
}
Expand Down
42 changes: 39 additions & 3 deletions src/jit/inst_branch_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,36 @@ pub extern "C" fn run_scheduler<const ARM7_HLE: bool>(asm: *mut JitAsm<{ ARM9 }>
cm.add_cycles(cycles);
if cm.check_events(asm.emu) && !ARM7_HLE {
let jit_asm_arm7 = unsafe { get_jit_asm_ptr::<{ ARM7 }>().as_mut_unchecked() };
jit_asm_arm7.runtime_data.clear_idle_loop();
jit_asm_arm7.runtime_data.set_idle_loop(false);
}
get_common_mut!(asm.emu).gpu.gpu_3d_regs.run_cmds(cm.get_cycles(), asm.emu);
}

fn run_scheduler_idle_loop<const ARM7_HLE: bool>(asm: &mut JitAsm<{ ARM9 }>) {
let cm = get_cm_mut!(asm.emu);

if ARM7_HLE {
cm.jump_to_next_event();
} else {
let jit_asm_arm7 = unsafe { get_jit_asm_ptr::<{ ARM7 }>().as_mut_unchecked() };
if likely(!get_cpu_regs!(jit_asm_arm7.emu, ARM7).is_halted() && !jit_asm_arm7.runtime_data.is_idle_loop()) {
unsafe { CURRENT_RUNNING_CPU = ARM7 };
cm.add_cycles(jit_asm_arm7.execute());
} else {
cm.jump_to_next_event();
};
unsafe { CURRENT_RUNNING_CPU = ARM9 };
}

if cm.check_events(asm.emu) && !ARM7_HLE {
let jit_asm_arm7 = unsafe { get_jit_asm_ptr::<{ ARM7 }>().as_mut_unchecked() };
jit_asm_arm7.runtime_data.set_idle_loop(false);
}
get_common_mut!(asm.emu).gpu.gpu_3d_regs.run_cmds(cm.get_cycles(), asm.emu);
}

#[naked]
unsafe extern "C" fn call_interrupt(entry: *const fn(), interrupt_sp_ptr: *mut usize) {
unsafe extern "C" fn call_interrupt(_: *const fn(), _: *mut usize) {
#[rustfmt::skip]
naked_asm!(
"push {{r4-r12,lr}}",
Expand Down Expand Up @@ -74,7 +97,7 @@ pub extern "C" fn handle_interrupt(asm: *mut JitAsm<{ ARM9 }>, target_pc: u32, c

asm.runtime_data.pre_cycle_count_sum = 0;
asm.runtime_data.set_in_interrupt(true);
asm.runtime_data.interrupt_lr = lr;
asm.runtime_data.push_return_stack(lr);
get_regs_mut!(asm.emu, ARM9).set_thumb(regs.pc & 1 == 1);
let jit_entry = get_jit!(asm.emu).get_jit_start_addr(align_guest_pc(regs.pc));
unsafe { call_interrupt(jit_entry as _, &mut asm.runtime_data.interrupt_sp) };
Expand Down Expand Up @@ -138,6 +161,19 @@ pub extern "C" fn pre_branch<const CPU: CpuType, const HAS_LR_RETURN: bool>(asm:
}
}

pub extern "C" fn handle_idle_loop<const ARM7_HLE: bool>(asm: *mut JitAsm<{ ARM9 }>, target_pre_cycle_count_sum: u16, current_pc: u32) {
let asm = unsafe { asm.as_mut_unchecked() };

let pc_og = get_regs!(asm.emu, ARM9).pc;
run_scheduler_idle_loop::<ARM7_HLE>(asm);
if get_regs!(asm.emu, ARM9).pc != pc_og {
handle_interrupt(asm, pc_og, current_pc);
}

asm.runtime_data.accumulated_cycles = 0;
asm.runtime_data.pre_cycle_count_sum = target_pre_cycle_count_sum;
}

pub unsafe extern "C" fn branch_reg<const CPU: CpuType, const HAS_LR_RETURN: bool>(total_cycles: u16, target_pc: u32, lr: u32, current_pc: u32) {
let asm = get_jit_asm_ptr::<CPU>().as_mut_unchecked();

Expand Down
10 changes: 4 additions & 6 deletions src/jit/jit_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ pub struct JitRuntimeData {
pub idle_loop_in_interrupt_return_stack_ptr: u8,
pub return_stack: [u32; RETURN_STACK_SIZE],
pub interrupt_sp: usize,
pub interrupt_lr: u32,
#[cfg(debug_assertions)]
branch_out_pc: u32,
}
Expand All @@ -76,7 +75,6 @@ impl JitRuntimeData {
idle_loop_in_interrupt_return_stack_ptr: 0,
return_stack: [0; RETURN_STACK_SIZE],
interrupt_sp: 0,
interrupt_lr: 0,
#[cfg(debug_assertions)]
branch_out_pc: u32::MAX,
};
Expand Down Expand Up @@ -138,8 +136,8 @@ impl JitRuntimeData {
self.idle_loop_in_interrupt_return_stack_ptr & 0x80 != 0
}

pub fn clear_idle_loop(&mut self) {
self.idle_loop_in_interrupt_return_stack_ptr &= !0x80;
pub fn set_idle_loop(&mut self, idle_loop: bool) {
self.idle_loop_in_interrupt_return_stack_ptr = (self.idle_loop_in_interrupt_return_stack_ptr & !0x80) | ((idle_loop as u8) << 7)
}

pub fn is_in_interrupt(&self) -> bool {
Expand Down Expand Up @@ -202,7 +200,7 @@ pub extern "C" fn hle_bios_uninterrupt<const CPU: CpuType>() {
} else {
match CPU {
ARM9 => {
if unlikely(asm.runtime_data.is_in_interrupt() && asm.runtime_data.interrupt_lr == regs.pc) {
if unlikely(asm.runtime_data.is_in_interrupt() && asm.runtime_data.pop_return_stack() == regs.pc) {
regs.set_thumb(regs.pc & 1 == 1);
unsafe {
std::arch::asm!(
Expand Down Expand Up @@ -353,7 +351,7 @@ fn emit_code_block_internal<const CPU: CpuType>(asm: &mut JitAsm<CPU>, guest_pc:
}

#[naked]
unsafe extern "C" fn call_jit_entry(entry: *const fn(), host_sp_ptr: *mut usize) {
unsafe extern "C" fn call_jit_entry(_: *const fn(), _: *mut usize) {
#[rustfmt::skip]
naked_asm!(
"push {{r4-r12,lr}}",
Expand Down
11 changes: 4 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,15 @@ fn execute_jit<const ARM7_HLE: bool>(emu: &mut UnsafeCell<Emu>) {
let gpu_3d_regs = &mut get_common_mut!(emu).gpu.gpu_3d_regs;

loop {
let arm9_cycles = if likely(!cpu_regs_arm9.is_halted() && !jit_asm_arm9.runtime_data.is_idle_loop()) {
let arm9_cycles = if likely(!cpu_regs_arm9.is_halted()) {
unsafe { CURRENT_RUNNING_CPU = ARM9 };
(jit_asm_arm9.execute() + 1) >> 1
} else {
0
};

if ARM7_HLE {
if unlikely(cpu_regs_arm9.is_halted() || jit_asm_arm9.runtime_data.is_idle_loop()) {
if unlikely(cpu_regs_arm9.is_halted()) {
cm.jump_to_next_event();
} else {
cm.add_cycles(arm9_cycles);
Expand All @@ -288,11 +288,8 @@ fn execute_jit<const ARM7_HLE: bool>(emu: &mut UnsafeCell<Emu>) {
}
}

if cm.check_events(emu) {
jit_asm_arm9.runtime_data.clear_idle_loop();
if !ARM7_HLE {
jit_asm_arm7.runtime_data.clear_idle_loop();
}
if cm.check_events(emu) && !ARM7_HLE {
jit_asm_arm7.runtime_data.set_idle_loop(false);
}

gpu_3d_regs.run_cmds(cm.get_cycles(), emu);
Expand Down

0 comments on commit 306aed9

Please sign in to comment.