Skip to content

Instantly share code, notes, and snippets.

@Enprogames
Created January 3, 2025 02:56
Show Gist options
  • Select an option

  • Save Enprogames/e3458f0ef0e0dfa2231c7e4ae02efbbb to your computer and use it in GitHub Desktop.

Select an option

Save Enprogames/e3458f0ef0e0dfa2231c7e4ae02efbbb to your computer and use it in GitHub Desktop.
Example of Rust Two-Way Struct Communication by Passing in Closure
use std::cell::RefCell;
use std::rc::Rc;
pub struct CPU {
memory_bus: Rc<RefCell<MemoryBus>>,
oam_dma_page: RefCell<u8>,
}
impl CPU {
pub fn new(memory_bus: Rc<RefCell<MemoryBus>>) -> Self {
Self {
memory_bus,
oam_dma_page: RefCell::new(0),
}
}
pub fn enter_oam_dma(&self, data: u8) {
println!(
"[CPU] Entering OAM DMA mode with page: 0x{:02X} ({})",
data, data
);
// Update CPU state using interior mutability
*self.oam_dma_page.borrow_mut() = data;
}
pub fn write(&self, address: u16, data: u8) {
println!(
"[CPU] Normal write to address: 0x{:04X} with data: 0x{:02X} ({})",
address, data, data
);
self.memory_bus.borrow_mut().write(address, data);
}
}
// Define the MemoryBus structure
pub struct MemoryBus {
oam_dma_callback: Option<Box<dyn FnMut(u8)>>,
}
impl MemoryBus {
pub fn new() -> Self {
Self {
oam_dma_callback: None,
}
}
// Method to set the OAM DMA callback
pub fn set_oam_dma_callback<F>(&mut self, callback: F)
where
F: FnMut(u8) + 'static,
{
self.oam_dma_callback = Some(Box::new(callback));
println!("[MemoryBus] OAM DMA callback has been set.");
}
pub fn write(&mut self, address: u16, data: u8) {
println!(
"[MemoryBus] Writing to address: 0x{:04X} with data: 0x{:02X} ({})",
address, data, data
);
if address == 0x4014 {
println!("[MemoryBus] Detected write to OAM DMA register (0x4014). Triggering DMA...");
if let Some(ref mut callback) = self.oam_dma_callback {
callback(data);
} else {
println!("[MemoryBus] No OAM DMA callback set!");
}
} else {
// Handle other memory writes
println!("[MemoryBus] Handling normal memory write.");
}
}
// The CPU should call this method every write cycle (odd cycles?) to write data for OAM DMA
pub fn write_oam_dma(&self, address: u16, data: u8) {
println!(
"[MemoryBus] Writing to OAM DMA address: 0x{:04X} with data: 0x{:02X} ({})",
address, data, data
);
}
}
pub struct NES {
pub cpu: Rc<RefCell<CPU>>,
pub memory_bus: Rc<RefCell<MemoryBus>>,
}
impl NES {
pub fn new() -> Self {
let memory_bus = Rc::new(RefCell::new(MemoryBus::new()));
let cpu = Rc::new(RefCell::new(CPU::new(memory_bus.clone())));
{
// Clone the Rc to move into the closure
let cpu_ref = Rc::clone(&cpu);
memory_bus.borrow_mut().set_oam_dma_callback(move |dma_value| {
println!("[Closure] OAM DMA callback invoked with value: 0x{:02X} ({})", dma_value, dma_value);
cpu_ref.borrow().enter_oam_dma(dma_value);
});
}
Self { cpu, memory_bus }
}
pub fn run_example(&mut self) {
println!("\n--- Running NES Example ---\n");
// Perform a normal write to a non-DMA address
let normal_address = 0x2000;
let normal_data = 0xAA;
self.cpu.borrow().write(normal_address, normal_data);
// Perform a write to the OAM DMA register
let dma_address = 0x4014;
let dma_data = 0x55;
println!("\n[Main] Performing OAM DMA write:");
self.cpu.borrow().write(dma_address, dma_data);
println!("\n--- NES Example Completed ---\n");
}
}
fn main() {
let mut nes = NES::new();
nes.run_example();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment