Skip to content

Commit

Permalink
DMA Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnsAnns committed Jun 15, 2024
1 parent c91f2e0 commit e899c67
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 7 deletions.
6 changes: 5 additions & 1 deletion src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod step;
pub mod interrupts;
mod joypad;
mod timer;
mod dma;
mod helpers;

/// 4.194304 MHz
Expand All @@ -36,7 +37,8 @@ pub struct CPU {
is_halted: bool,
stop_mode: bool,
pub instruction: i32,

dma_active: bool, // Whether a DMA has been requested
dma_current_offset: u8, // The current line offset based on the DMA register being copied
}

/// Note, please look at the relevant modules for the actual implementations
Expand All @@ -55,6 +57,8 @@ impl CPU {
is_halted: false,
stop_mode: false,
instruction: 0,
dma_active: false,
dma_current_offset: 0,
}
}
}
35 changes: 35 additions & 0 deletions src/cpu/dma.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::CPU;

const DMA_REGISTER_ADDR: u16 = 0xFF46;
const OAM_BASE: u16 = 0xFE00;

impl CPU {
/// Handles the DMA circuit
/// See: https://gbdev.io/pandocs/OAM_DMA_Transfer.html
pub fn dma_routine(&mut self) {
if !self.dma_active {
return;
}

// Get DMA base from memory
let dma_base = (self.memory.read_byte(DMA_REGISTER_ADDR) as u16) << 8;

// Get the current byte to be read by combining the base + dma_current_offset
let source_addr = dma_base + self.dma_current_offset as u16;
let target_addr = OAM_BASE + self.dma_current_offset as u16;

// Write from memory to OAM
self.memory.write_byte(target_addr, self.memory.read_byte(source_addr));

// Append offset
let (_, overflow) = self.dma_current_offset.overflowing_add(1);

// If the offset overflows we reached the end
if overflow {
self.dma_active = false;
self.dma_current_offset = 0;
} else {
self.dma_current_offset += 1;
}
}
}
5 changes: 4 additions & 1 deletion src/cpu/instructions/bit_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use crate::cpu::{
};

#[cfg(test)]
use crate::test_helpers::{assert_correct_instruction_step};
use crate::test_helpers::assert_correct_instruction_step;

#[cfg(test)]
use crate::cpu::Instructions;

impl CPU {
/// check if bit 'bit' in 8bit-register target is set and set zero flag if not
Expand Down
5 changes: 4 additions & 1 deletion src/cpu/instructions/jumps_subroutines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use crate::cpu::{
};

#[cfg(test)]
use crate::test_helpers::{assert_correct_instruction_step};
use crate::test_helpers::assert_correct_instruction_step;

#[cfg(test)]
use crate::cpu::Instructions;

impl CPU {
///loads value in register HL in PC, realising a jump
Expand Down
5 changes: 4 additions & 1 deletion src/cpu/instructions/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use crate::cpu::{
};

#[cfg(test)]
use crate::test_helpers::{assert_correct_instruction_step};
use crate::test_helpers::assert_correct_instruction_step;

#[cfg(test)]
use crate::cpu::Instructions;

impl CPU {
/// loads(copies) the value of the source 8bit-register into the target 8bit-register
Expand Down
3 changes: 3 additions & 0 deletions src/cpu/instructions/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use crate::cpu::CPU;
#[cfg(test)]
use crate::test_helpers::{assert_correct_instruction_decode, assert_correct_instruction_step};

#[cfg(test)]
use crate::cpu::Instructions;

use super::{ConditionCodes, FlagState, InstructionResult, Register8Bit};

impl CPU {
Expand Down
12 changes: 12 additions & 0 deletions src/cpu/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,18 @@ impl CPU {
self.last_execution_time = std::time::Instant::now();
self.cycles += self.last_step_result.cycles as u64;

// Check whether a DMA routine has been requested
if self.memory.is_dma_requested() {
self.dma_active = true;
self.dma_current_offset = 0;
self.memory.reset_dma_request();
}

// Do DMA for cycles used
for _ in 0..self.last_step_result.cycles {
self.dma_routine();
}

Ok(&self.last_step_result)
}

Expand Down
5 changes: 2 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extern crate simple_log;

use crate::cpu::registers::{Register16Bit, Register8Bit};

const TIME_PER_FRAME: f32 = 1000.0 / 30.0;
const TIME_PER_FRAME: f32 = 1000.0 / 60.0;

const DUMP_GAMEBOY_DOCTOR_LOG: bool = false;
#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -143,10 +143,9 @@ async fn main() {

// Redraw UI at 30 frames per second
if (ppu_time.elapsed().as_millis() as f32) >= TIME_PER_FRAME {
// Also only poll inputs at that interval
// Poll inputs
cpu.poll_inputs();
cpu.blarg_print();

ppu_time = time::Instant::now();

// Inform about the time it took to render the frame
Expand Down
2 changes: 2 additions & 0 deletions src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Memory {
memory: [u8; MEMORY_SIZE],
pub boot_rom_enabled: bool,
boot_rom: [u8; ROM_SIZE],
dma_requested: bool,
}

/// Implementation of the Memory
Expand All @@ -43,6 +44,7 @@ impl Memory {
memory: [0; MEMORY_SIZE],
boot_rom_enabled: enable_bootrom,
boot_rom,
dma_requested: false
}
}
}
7 changes: 7 additions & 0 deletions src/memory/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use std::io::Write;
use super::{Memory, MEMORY_SIZE};

impl Memory {
pub fn is_dma_requested(&self) -> bool {
self.dma_requested
}

pub fn reset_dma_request(&mut self) {
self.dma_requested = false;
}

pub fn is_boot_rom_enabled(&self) -> bool {
self.boot_rom_enabled
Expand Down
5 changes: 5 additions & 0 deletions src/memory/raw_memory_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ impl Memory {
let prev = self.read_byte(address);
self.memory[address as usize] = (value & 0xF0) | (prev & 0xF);
},
// OAM DMA Register
0xFF46 => {
self.dma_requested = true;
self.memory[address as usize] = value
}
_ => self.memory[address as usize] = value,
}
}
Expand Down

0 comments on commit e899c67

Please sign in to comment.