More registers!

This commit is contained in:
Lokathor 2018-12-31 16:28:20 -07:00
parent 4217a2b795
commit f825d78e71
7 changed files with 406 additions and 3 deletions

View file

@ -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 <zefria@gmail.com>", "Thomas Winwood <twwinwood@gmail.com>"]
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"

View file

@ -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;

66
src/io/color_blend.rs Normal file
View file

@ -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<ColorEffectSetting> = 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<None, AlphaBlending, BrightnessIncrease, BrightnessDecrease>,
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<AlphaBlendingSetting> = 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<BrightnessSetting> = unsafe { VolAddress::new_unchecked(0x400_0054) };
newtype! {
BrightnessSetting, u32
}
impl BrightnessSetting {
phantom_fields! {
self.0: u32,
evy_coefficient: 0-4,
}
}

View file

@ -33,7 +33,6 @@ impl DisplayControlSetting {
phantom_fields! {
self.0: u16,
mode: 0-2=DisplayMode<Mode0, Mode1, Mode2, Mode3, Mode4, Mode5>,
cgb_mode: 3,
frame1: 4,
hblank_interval_free: 5,
oam_memory_1d: 6,

View file

@ -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<u16> = unsafe { VolAddress::new_unchecked(0x400_00B8) };
/// DMA 0 Control, read/write.
const DMA0CNT_H: VolAddress<DMAControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_00C4) };
/// DMA 1 Control, read/write.
const DMA1CNT_H: VolAddress<DMAControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_00D0) };
/// DMA 2 Control, read/write.
const DMA2CNT_H: VolAddress<DMAControlSetting> = 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 {

89
src/io/window.rs Normal file
View file

@ -0,0 +1,89 @@
//! Module that holds stuff for the Window ability.
use super::*;
/// Window 0 Horizontal Dimensions (W)
pub const WIN0H: VolAddress<HorizontalWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0040) };
/// Window 1 Horizontal Dimensions (W)
pub const WIN1H: VolAddress<HorizontalWindowSetting> = 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<VerticalWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0044) };
/// Window 1 Vertical Dimensions (W)
pub const WIN1V: VolAddress<VerticalWindowSetting> = 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<InsideWindowSetting> = 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<OutsideWindowSetting> = 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,
}
}

View file

@ -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::<u16>();
va_u16.cast::<OBJAttr0>().write(attributes.attr0);
va_u16.offset(1).cast::<OBJAttr1>().write(attributes.attr1);
va_u16.offset(2).cast::<OBJAttr2>().write(attributes.attr2);
})
}
pub fn read_obj_attributes(slot: usize) -> Option<ObjectAttributes> {
OBJ_ATTR_APPROX.get(slot).map(|va| unsafe {
let va_u16 = va.cast::<u16>();
let attr0 = va_u16.cast::<OBJAttr0>().read();
let attr1 = va_u16.offset(1).cast::<OBJAttr1>().read();
let attr2 = va_u16.offset(2).cast::<OBJAttr2>().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::<i16>();
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<AffineParameters> {
AFFINE_PARAMS_APPROX.get(slot).map(|va| unsafe {
let va_i16 = va.cast::<i16>();
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 }
})
}