mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
More registers!
This commit is contained in:
parent
4217a2b795
commit
f825d78e71
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gba"
|
name = "gba"
|
||||||
description = "A crate (and book) for making GBA games with Rust."
|
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>"]
|
authors = ["Lokathor <zefria@gmail.com>", "Thomas Winwood <twwinwood@gmail.com>"]
|
||||||
repository = "https://github.com/rust-console/gba"
|
repository = "https://github.com/rust-console/gba"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -9,7 +9,7 @@ keywords = ["gba"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
publish = false
|
#publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
typenum = "1.10"
|
typenum = "1.10"
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub mod background;
|
pub mod background;
|
||||||
|
pub mod color_blend;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
pub mod dma;
|
pub mod dma;
|
||||||
pub mod keypad;
|
pub mod keypad;
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
pub mod timers;
|
pub mod timers;
|
||||||
|
pub mod window;
|
||||||
|
|
66
src/io/color_blend.rs
Normal file
66
src/io/color_blend.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,6 @@ impl DisplayControlSetting {
|
||||||
phantom_fields! {
|
phantom_fields! {
|
||||||
self.0: u16,
|
self.0: u16,
|
||||||
mode: 0-2=DisplayMode<Mode0, Mode1, Mode2, Mode3, Mode4, Mode5>,
|
mode: 0-2=DisplayMode<Mode0, Mode1, Mode2, Mode3, Mode4, Mode5>,
|
||||||
cgb_mode: 3,
|
|
||||||
frame1: 4,
|
frame1: 4,
|
||||||
hblank_interval_free: 5,
|
hblank_interval_free: 5,
|
||||||
oam_memory_1d: 6,
|
oam_memory_1d: 6,
|
||||||
|
|
183
src/io/dma.rs
183
src/io/dma.rs
|
@ -124,6 +124,189 @@ pub enum DMAStartTiming {
|
||||||
Special = 3,
|
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.
|
/// This is the "general purpose" DMA unit, with the fewest limits.
|
||||||
pub struct DMA3;
|
pub struct DMA3;
|
||||||
impl DMA3 {
|
impl DMA3 {
|
||||||
|
|
89
src/io/window.rs
Normal file
89
src/io/window.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
64
src/oam.rs
64
src/oam.rs
|
@ -127,3 +127,67 @@ impl OBJAttr2 {
|
||||||
palbank: 12-15,
|
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 }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue