From f825d78e712e357264abdccf33881fa76a7456fe Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 31 Dec 2018 16:28:20 -0700 Subject: [PATCH] More registers! --- Cargo.toml | 4 +- src/io.rs | 2 + src/io/color_blend.rs | 66 +++++++++++++++ src/io/display.rs | 1 - src/io/dma.rs | 183 ++++++++++++++++++++++++++++++++++++++++++ src/io/window.rs | 89 ++++++++++++++++++++ src/oam.rs | 64 +++++++++++++++ 7 files changed, 406 insertions(+), 3 deletions(-) create mode 100644 src/io/color_blend.rs create mode 100644 src/io/window.rs diff --git a/Cargo.toml b/Cargo.toml index 25d435d..d099971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gba" description = "A crate (and book) for making GBA games with Rust." -version = "0.3.0-pre" +version = "0.3.0" authors = ["Lokathor ", "Thomas Winwood "] repository = "https://github.com/rust-console/gba" readme = "README.md" @@ -9,7 +9,7 @@ keywords = ["gba"] edition = "2018" license = "Apache-2.0" -publish = false +#publish = false [dependencies] typenum = "1.10" diff --git a/src/io.rs b/src/io.rs index 3582e1b..076813b 100644 --- a/src/io.rs +++ b/src/io.rs @@ -9,8 +9,10 @@ use super::*; pub mod background; +pub mod color_blend; pub mod display; pub mod dma; pub mod keypad; pub mod sound; pub mod timers; +pub mod window; diff --git a/src/io/color_blend.rs b/src/io/color_blend.rs new file mode 100644 index 0000000..a1d041f --- /dev/null +++ b/src/io/color_blend.rs @@ -0,0 +1,66 @@ +//! Module that holds stuff for the color blending ability. + +use super::*; + +/// Color Special Effects Selection (R/W) +pub const BLDCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0050) }; + +newtype! { + ColorEffectSetting, u16 +} + +impl ColorEffectSetting { + phantom_fields! { + self.0: u16, + bg0_1st_target_pixel: 0, + bg1_1st_target_pixel: 1, + bg2_1st_target_pixel: 2, + bg3_1st_target_pixel: 3, + obj_1st_target_pixel: 4, + backdrop_1st_target_pixel: 5, + color_special_effect: 6-7=ColorSpecialEffect, + bg0_2nd_target_pixel: 8, + bg1_2nd_target_pixel: 9, + bg2_2nd_target_pixel: 10, + bg3_2nd_target_pixel: 11, + obj_2nd_target_pixel: 12, + backdrop_2nd_target_pixel: 13, + } +} + +newtype_enum! { + ColorSpecialEffect = u16, + None = 0, + AlphaBlending = 1, + BrightnessIncrease = 2, + BrightnessDecrease = 3, +} + +/// Alpha Blending Coefficients (R/W) (not W) +pub const BLDALPHA: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0052) }; + +newtype! { + AlphaBlendingSetting, u16 +} + +impl AlphaBlendingSetting { + phantom_fields! { + self.0: u16, + eva_coefficient: 0-4, + evb_coefficient: 8-12, + } +} + +/// Brightness (Fade-In/Out) Coefficient (W) (not R/W) +pub const BLDY: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0054) }; + +newtype! { + BrightnessSetting, u32 +} + +impl BrightnessSetting { + phantom_fields! { + self.0: u32, + evy_coefficient: 0-4, + } +} diff --git a/src/io/display.rs b/src/io/display.rs index f2f5ddf..058143e 100644 --- a/src/io/display.rs +++ b/src/io/display.rs @@ -33,7 +33,6 @@ impl DisplayControlSetting { phantom_fields! { self.0: u16, mode: 0-2=DisplayMode, - cgb_mode: 3, frame1: 4, hblank_interval_free: 5, oam_memory_1d: 6, diff --git a/src/io/dma.rs b/src/io/dma.rs index bea9669..38f7567 100644 --- a/src/io/dma.rs +++ b/src/io/dma.rs @@ -124,6 +124,189 @@ pub enum DMAStartTiming { Special = 3, } +pub struct DMA0; +impl DMA0 { + /// DMA 0 Source Address, read only. + const DMA0SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00B0) }; + /// DMA 0 Destination Address, read only. + const DMA0DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00B4) }; + /// DMA 0 Word Count, read only. + const DMA0CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00B8) }; + /// DMA 0 Control, read/write. + const DMA0CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00BA) }; + + /// Assigns the source register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The source pointer must be aligned and valid to read from. + pub unsafe fn set_source(src: *const u32) { + Self::DMA0SAD.write(src) + } + + /// Assigns the destination register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The source pointer must be aligned and valid to write to. + pub unsafe fn set_dest(dest: *mut u32) { + Self::DMA0DAD.write(dest) + } + + /// Assigns the count register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The count given must specify a valid number of units to write, starting at + /// the assigned destination address. + pub unsafe fn set_count(count: u16) { + Self::DMA0CNT_L.write(count) + } + + /// Reads the current control setting. + pub fn control() -> DMAControlSetting { + Self::DMA0CNT_H.read() + } + + /// Writes the control setting given. + /// + /// # Safety + /// + /// You must ensure that the Source, Destination, and Count values are set + /// correctly **before** you activate the Enable bit. + pub unsafe fn set_control(setting: DMAControlSetting) { + Self::DMA0CNT_H.write(setting) + } +} + +pub struct DMA1; +impl DMA1 { + /// DMA 1 Source Address, read only. + const DMA1SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00BC) }; + /// DMA 1 Destination Address, read only. + const DMA1DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00C0) }; + /// DMA 1 Word Count, read only. + const DMA1CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00C4) }; + /// DMA 1 Control, read/write. + const DMA1CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00C6) }; + + /// Assigns the source register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The source pointer must be aligned and valid to read from. + pub unsafe fn set_source(src: *const u32) { + Self::DMA1SAD.write(src) + } + + /// Assigns the destination register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The source pointer must be aligned and valid to write to. + pub unsafe fn set_dest(dest: *mut u32) { + Self::DMA1DAD.write(dest) + } + + /// Assigns the count register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The count given must specify a valid number of units to write, starting at + /// the assigned destination address. + pub unsafe fn set_count(count: u16) { + Self::DMA1CNT_L.write(count) + } + + /// Reads the current control setting. + pub fn control() -> DMAControlSetting { + Self::DMA1CNT_H.read() + } + + /// Writes the control setting given. + /// + /// # Safety + /// + /// You must ensure that the Source, Destination, and Count values are set + /// correctly **before** you activate the Enable bit. + pub unsafe fn set_control(setting: DMAControlSetting) { + Self::DMA1CNT_H.write(setting) + } +} + +pub struct DMA2; +impl DMA2 { + /// DMA 2 Source Address, read only. + const DMA2SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00C8) }; + /// DMA 2 Destination Address, read only. + const DMA2DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00CC) }; + /// DMA 2 Word Count, read only. + const DMA2CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00D0) }; + /// DMA 2 Control, read/write. + const DMA2CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00D2) }; + + /// Assigns the source register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The source pointer must be aligned and valid to read from. + pub unsafe fn set_source(src: *const u32) { + Self::DMA2SAD.write(src) + } + + /// Assigns the destination register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The source pointer must be aligned and valid to write to. + pub unsafe fn set_dest(dest: *mut u32) { + Self::DMA2DAD.write(dest) + } + + /// Assigns the count register. + /// + /// This register is read only, so it is not exposed directly. + /// + /// # Safety + /// + /// The count given must specify a valid number of units to write, starting at + /// the assigned destination address. + pub unsafe fn set_count(count: u16) { + Self::DMA2CNT_L.write(count) + } + + /// Reads the current control setting. + pub fn control() -> DMAControlSetting { + Self::DMA2CNT_H.read() + } + + /// Writes the control setting given. + /// + /// # Safety + /// + /// You must ensure that the Source, Destination, and Count values are set + /// correctly **before** you activate the Enable bit. + pub unsafe fn set_control(setting: DMAControlSetting) { + Self::DMA2CNT_H.write(setting) + } +} + /// This is the "general purpose" DMA unit, with the fewest limits. pub struct DMA3; impl DMA3 { diff --git a/src/io/window.rs b/src/io/window.rs new file mode 100644 index 0000000..0e09794 --- /dev/null +++ b/src/io/window.rs @@ -0,0 +1,89 @@ +//! Module that holds stuff for the Window ability. + +use super::*; + +/// Window 0 Horizontal Dimensions (W) +pub const WIN0H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0040) }; + +/// Window 1 Horizontal Dimensions (W) +pub const WIN1H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0042) }; + +newtype! { + HorizontalWindowSetting, u16 +} + +impl HorizontalWindowSetting { + phantom_fields! { + self.0: u16, + col_end: 0-7, + col_start: 8-15, + } +} + +/// Window 0 Vertical Dimensions (W) +pub const WIN0V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0044) }; + +/// Window 1 Vertical Dimensions (W) +pub const WIN1V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0046) }; + +newtype! { + VerticalWindowSetting, u16 +} + +impl VerticalWindowSetting { + phantom_fields! { + self.0: u16, + row_end: 0-7, + row_start: 8-15, + } +} + +/// Control of Inside of Window(s) (R/W) +pub const WININ: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0048) }; + +newtype! { + InsideWindowSetting, u16 +} + +impl InsideWindowSetting { + phantom_fields! { + self.0: u16, + win0_bg0: 0, + win0_bg1: 1, + win0_bg2: 2, + win0_bg3: 3, + win0_obj: 4, + win0_color_special: 5, + win1_bg0: 8, + win1_bg1: 9, + win1_bg2: 10, + win1_bg3: 11, + win1_obj: 12, + win1_color_special: 13, + } +} + +/// Control of Outside of Windows & Inside of OBJ Window (R/W) +pub const WINOUT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_004A) }; + +newtype! { + OutsideWindowSetting, u16 +} + +impl OutsideWindowSetting { + phantom_fields! { + self.0: u16, + outside_bg0: 0, + outside_bg1: 1, + outside_bg2: 2, + outside_bg3: 3, + outside_obj: 4, + outside_color_special: 5, + obj_win_bg0: 8, + obj_win_bg1: 9, + obj_win_bg2: 10, + obj_win_bg3: 11, + obj_win_obj: 12, + obj_win_color_special: 13, + } +} diff --git a/src/oam.rs b/src/oam.rs index 897e76e..fe7f239 100644 --- a/src/oam.rs +++ b/src/oam.rs @@ -127,3 +127,67 @@ impl OBJAttr2 { palbank: 12-15, } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ObjectAttributes { + attr0: OBJAttr0, + attr1: OBJAttr1, + attr2: OBJAttr2, +} + +/// The object attributes, but there are gaps in the array, so we must not +/// expose this directly. +const OBJ_ATTR_APPROX: VolAddressBlock<[u16; 4]> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x700_0000), 128) }; + +pub fn write_obj_attributes(slot: usize, attributes: ObjectAttributes) -> Option<()> { + OBJ_ATTR_APPROX.get(slot).map(|va| unsafe { + let va_u16 = va.cast::(); + va_u16.cast::().write(attributes.attr0); + va_u16.offset(1).cast::().write(attributes.attr1); + va_u16.offset(2).cast::().write(attributes.attr2); + }) +} + +pub fn read_obj_attributes(slot: usize) -> Option { + OBJ_ATTR_APPROX.get(slot).map(|va| unsafe { + let va_u16 = va.cast::(); + let attr0 = va_u16.cast::().read(); + let attr1 = va_u16.offset(1).cast::().read(); + let attr2 = va_u16.offset(2).cast::().read(); + ObjectAttributes { attr0, attr1, attr2 } + }) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AffineParameters { + pa: i16, + pb: i16, + pc: i16, + pd: i16, +} +// TODO: find the correct fixed-point type here. + +/// The object attributes, but there are gaps in the array, so we must not +/// expose this directly. +const AFFINE_PARAMS_APPROX: VolAddressBlock<[i16; 16]> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x700_0000), 32) }; + +pub fn write_affine_parameters(slot: usize, params: AffineParameters) -> Option<()> { + AFFINE_PARAMS_APPROX.get(slot).map(|va| unsafe { + let va_i16 = va.cast::(); + va_i16.offset(3).write(params.pa); + va_i16.offset(7).write(params.pb); + va_i16.offset(11).write(params.pc); + va_i16.offset(15).write(params.pd); + }) +} + +pub fn read_affine_parameters(slot: usize) -> Option { + AFFINE_PARAMS_APPROX.get(slot).map(|va| unsafe { + let va_i16 = va.cast::(); + let pa = va_i16.offset(3).read(); + let pb = va_i16.offset(7).read(); + let pc = va_i16.offset(11).read(); + let pd = va_i16.offset(15).read(); + AffineParameters { pa, pb, pc, pd } + }) +}