diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 202b23f..f1e5336 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -15,7 +15,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Bitmap3).with_bg2(true); + const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Mode3).with_bg2(true); DISPCNT.write(SETTING); Mode3::write_pixel(120, 80, Color::from_rgb(31, 0, 0)); Mode3::write_pixel(136, 80, Color::from_rgb(0, 31, 0)); diff --git a/examples/light_cycle.rs b/examples/light_cycle.rs index 4ba4698..29ad8b6 100644 --- a/examples/light_cycle.rs +++ b/examples/light_cycle.rs @@ -18,7 +18,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Bitmap3).with_bg2(true); + const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Mode3).with_bg2(true); DISPCNT.write(SETTING); let mut px = Mode3::SCREEN_WIDTH / 2; diff --git a/examples/mgba_panic_handler.rs b/examples/mgba_panic_handler.rs index 7711460..a51c0c9 100644 --- a/examples/mgba_panic_handler.rs +++ b/examples/mgba_panic_handler.rs @@ -22,7 +22,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Bitmap3).with_bg2(true); + const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Mode3).with_bg2(true); DISPCNT.write(SETTING); Mode3::write_pixel(120, 80, Color::from_rgb(31, 0, 0)); Mode3::write_pixel(136, 80, Color::from_rgb(0, 31, 0)); diff --git a/src/io/background.rs b/src/io/background.rs index 1b38309..dad56f1 100644 --- a/src/io/background.rs +++ b/src/io/background.rs @@ -2,36 +2,153 @@ use super::*; +// BG0 Control. Read/Write. +pub const BG0CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0008) }; +// BG1 Control. Read/Write. +pub const BG1CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000A) }; +// BG2 Control. Read/Write. +pub const BG2CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000C) }; +// BG3 Control. Read/Write. +pub const BG3CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000E) }; + newtype! { - /// A newtype over the various display control options that you have on a GBA. + /// Allows configuration of a background layer. + /// + /// Bits 0-1: BG Priority (lower number is higher priority, like an index) + /// Bits 2-3: Character Base Block (0 through 3, 16k each) + /// Bit 6: Mosaic mode + /// Bit 7: is 8bpp + /// Bit 8-12: Screen Base Block (0 through 31, 2k each) + /// Bit 13: Display area overflow wraps (otherwise transparent, affine only) + /// Bit 14-15: Screen Size #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] BackgroundControlSetting, u16 } impl BackgroundControlSetting { pub const fn from_screen_base_block(screen_base_block: u16) -> Self { - BackgroundControlSetting(screen_base_block << 8) // TODO: mask this for correctness + BackgroundControlSetting((screen_base_block & 31) << 8) } // - pub const PRIORITY_MASK: u16 = 0b11 << 0; - pub const fn priority(self) -> u16 { - self.0 & Self::PRIORITY_MASK - } - pub const fn with_priority(self, priority: u16) -> Self { - BackgroundControlSetting((self.0 & !Self::PRIORITY_MASK) | priority) - } + bool_bits!(u16, [(6, mosaic), (7, is_8bpp), (13, display_overflow_wrapping)]); - pub const CHARACTER_BASE_BLOCK_MASK: u16 = 0b11 << 2; - pub const fn character_base_block(self) -> u16 { - (self.0 & Self::CHARACTER_BASE_BLOCK_MASK) >> 2 - } - pub const fn with_character_base_block(self, character_base_block: u16) -> Self { - BackgroundControlSetting((self.0 & !Self::CHARACTER_BASE_BLOCK_MASK) | ((character_base_block << 2) & Self::CHARACTER_BASE_BLOCK_MASK)) - } + multi_bits!( + u16, + [ + (0, 2, bg_priority), + (2, 2, char_base_block), + (8, 5, screen_base_block), + (2, 2, size, BGSize, Zero, One, Two, Three), + ] + ); } -pub const BG0CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0008) }; -pub const BG1CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000A) }; -pub const BG2CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000C) }; -pub const BG3CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000E) }; +/// The size of a background. +/// +/// The meaning changes depending on if the background is Text or Affine mode. +/// +/// * In text mode, the screen base block determines where to start reading the +/// tile arrangement data (2k). Size Zero gives one screen block of use. Size +/// One and Two cause two of them to be used (horizontally or vertically, +/// respectively). Size Three is four blocks used, [0,1] above and then [2,3] +/// below. Each screen base block used is always a 32x32 tile grid. +/// * In affine mode, the screen base block determines where to start reading +/// data followed by the size of data as shown. The number of tiles varies +/// according to the size used. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum BGSize { + /// * Text: 256x256px (2k) + /// * Affine: 128x128px (256b) + Zero = 0, + /// * Text: 512x256px (4k) + /// * Affine: 256x256px (1k) + One = 1, + /// * Text: 256x512px (4k) + /// * Affine: 512x512px (4k) + Two = 2, + /// * Text: 512x512px (8k) + /// * Affine: 1024x1024px (16k) + Three = 3, +} + +// BG0 X-Offset. Write only. Text mode only. 9 bits. +pub const BG0HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0010) }; +// BG0 Y-Offset. Write only. Text mode only. 9 bits. +pub const BG0VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0012) }; + +// BG1 X-Offset. Write only. Text mode only. 9 bits. +pub const BG1HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0012) }; +// BG1 Y-Offset. Write only. Text mode only. 9 bits. +pub const BG1VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0012) }; + +// BG2 X-Offset. Write only. Text mode only. 9 bits. +pub const BG2HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0018) }; +// BG2 Y-Offset. Write only. Text mode only. 9 bits. +pub const BG2VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_001A) }; + +// BG3 X-Offset. Write only. Text mode only. 9 bits. +pub const BG3HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_001C) }; +// BG3 Y-Offset. Write only. Text mode only. 9 bits. +pub const BG3VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_001E) }; + +// TODO: affine registers: +// BG2X_L +// BG2X_H +// BG2Y_L +// BG2Y_H +// BG2PA +// BG2PB +// BG2PC +// BG2PD +// BG3PA +// BG3PB +// BG3PC +// BG3PD + +// TODO: windowing +// pub const WIN0H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0040) }; +// pub const WIN1H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0042) }; +// pub const WIN0V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0044) }; +// pub const WIN1V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0046) }; +// pub const WININ: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0048) }; +// pub const WINOUT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_004A) }; + +/// Global mosaic effect control. Write-only. +pub const MOSAIC: VolAddress = unsafe { VolAddress::new_unchecked(0x400_004C) }; + +newtype! { + /// Allows control of the Mosaic effect. + /// + /// Values are the _increase_ for each top-left pixel to be duplicated in the + /// final result. If you want to duplicate some other pixel than the top-left, + /// you can offset the background or object by an appropriate amount. + /// + /// 0) No effect (1+0) + /// 1) Each pixel becomes 2 pixels (1+1) + /// 2) Each pixel becomes 3 pixels (1+2) + /// 3) Each pixel becomes 4 pixels (1+3) + /// + /// * Bits 0-3: BG mosaic horizontal increase + /// * Bits 4-7: BG mosaic vertical increase + /// * Bits 8-11: Object mosaic horizontal increase + /// * Bits 12-15: Object mosaic vertical increase + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] + MosaicSetting, u16 +} +impl MosaicSetting { + multi_bits!( + u16, + [ + (0, 4, bg_horizontal_inc), + (4, 4, bg_vertical_inc), + (8, 4, obj_horizontal_inc), + (12, 4, obj_vertical_inc), + ] + ); +} + +// pub const BLDCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0050) }; +// pub const BLDALPHA: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0052) }; +// pub const BLDY: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0054) }; diff --git a/src/io/display.rs b/src/io/display.rs index 831d315..353dffa 100644 --- a/src/io/display.rs +++ b/src/io/display.rs @@ -4,18 +4,35 @@ use super::*; /// LCD Control. Read/Write. /// -/// * [gbatek entry](http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol) +/// The "force vblank" bit is always set when your rust code first executes. pub const DISPCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0000) }; newtype!( - /// A newtype over the various display control options that you have on a GBA. + /// Setting for the display control register. + /// + /// * 0-2: `DisplayMode` + /// * 3: CGB mode flag + /// * 4: Display frame 1 (Modes 4/5 only) + /// * 5: "hblank interval free", allows full access to OAM during hblank + /// * 6: Object tile memory 1-dimensional + /// * 7: Force vblank + /// * 8: Display bg0 layer + /// * 9: Display bg1 layer + /// * 10: Display bg2 layer + /// * 11: Display bg3 layer + /// * 12: Display objects layer + /// * 13: Window 0 display + /// * 14: Window 1 display + /// * 15: Object window #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] - DisplayControlSetting, u16 + DisplayControlSetting, + u16 ); #[allow(missing_docs)] impl DisplayControlSetting { - bool_bits!(u16, + bool_bits!( + u16, [ (3, cgb_mode), (4, frame1), @@ -33,36 +50,46 @@ impl DisplayControlSetting { ] ); - multi_bits!( - u16, - [ - (0, 3, mode, DisplayMode, Tiled0, Tiled1, Tiled2, Bitmap3, Bitmap4, Bitmap5 ) - ] - ); + multi_bits!(u16, [(0, 3, mode, DisplayMode, Mode0, Mode1, Mode2, Mode3, Mode4, Mode5)]); } /// The six display modes available on the GBA. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u16)] pub enum DisplayMode { - /// This basically allows for the most different things at once (all layers, - /// 1024 tiles, two palette modes, etc), but you can't do affine - /// transformations. - Tiled0 = 0, - /// This is a mix of `Tile0` and `Tile2`: BG0 and BG1 run as if in `Tiled0`, - /// and BG2 runs as if in `Tiled2`. - Tiled1 = 1, - /// This allows affine transformations, but only uses BG2 and BG3. - Tiled2 = 2, - /// This is the basic bitmap draw mode. The whole screen is a single bitmap. - /// Uses BG2 only. - Bitmap3 = 3, - /// This uses _paletted color_ so that there's enough space to have two pages - /// at _full resolution_, allowing page flipping. Uses BG2 only. - Bitmap4 = 4, - /// This uses _reduced resolution_ so that there's enough space to have two - /// pages with _full color_, allowing page flipping. Uses BG2 only. - Bitmap5 = 5, + /// * Affine: No + /// * Layers: 0/1/2/3 + /// * Size(px): 256x256 to 512x512 + /// * Tiles: 1024 + /// * Palette Modes: 4bpp or 8bpp + Mode0 = 0, + /// * BG0 / BG1: As Mode0 + /// * BG2: As Mode2 + Mode1 = 1, + /// * Affine: Yes + /// * Layers: 2/3 + /// * Size(px): 128x128 to 1024x1024 + /// * Tiles: 256 + /// * Palette Modes: 8bpp + Mode2 = 2, + /// * Affine: Yes + /// * Layers: 2 + /// * Size(px): 240x160 (1 page) + /// * Bitmap + /// * Full Color + Mode3 = 3, + /// * Affine: Yes + /// * Layers: 2 + /// * Size(px): 240x160 (2 pages) + /// * Bitmap + /// * Palette Modes: 8bpp + Mode4 = 4, + /// * Affine: Yes + /// * Layers: 2 + /// * Size(px): 160x128 (2 pages) + /// * Bitmap + /// * Full Color + Mode5 = 5, } /// Assigns the given display control setting. @@ -74,8 +101,32 @@ pub fn display_control() -> DisplayControlSetting { DISPCNT.read() } -/// If the `VCOUNT` register reads equal to or above this then you're in vblank. -pub const VBLANK_SCANLINE: u16 = 160; +/// Display Status and IRQ Control. +pub const DISPSTAT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0004) }; + +newtype!( + /// A newtype over display status and interrupt control values. + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] + DisplayStatusSetting, + u16 +); + +#[allow(missing_docs)] +impl DisplayStatusSetting { + bool_bits!( + u16, + [ + (0, vblank_flag), + (1, hblank_flag), + (2, vcounter_flag), + (3, vblank_irq_enable), + (4, hblank_irq_enable), + (5, vcounter_irq_enable), + ] + ); + + multi_bits!(u16, [(8, 8, vcount_setting)]); +} /// Vertical Counter (LY). Read only. /// @@ -84,6 +135,9 @@ pub const VBLANK_SCANLINE: u16 = 160; /// is in a "vertical blank" period. pub const VCOUNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0006) }; +/// If the `VCOUNT` register reads equal to or above this then you're in vblank. +pub const VBLANK_SCANLINE: u16 = 160; + /// Obtains the current `VCOUNT` value. pub fn vcount() -> u16 { VCOUNT.read() diff --git a/src/oam.rs b/src/oam.rs new file mode 100644 index 0000000..ef7cb41 --- /dev/null +++ b/src/oam.rs @@ -0,0 +1,100 @@ +//! Types and declarations for the Object Attribute Memory (`OAM`). + +newtype! { + // TODO + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] + OBJAttr0, u16 +} +impl OBJAttr0 { + bool_bits!( + u16, + [ + (12, mosaic), + (13, is_8bpp), + ] + ); + + multi_bits!( + u16, + [ + (0, 8, y_coordinate), + (8, 2, obj_rendering, ObjectRender, Normal, Affine, Disabled, DoubleAreaAffine), + (10, 2, obj_mode, ObjectMode, Normal, SemiTransparent, OBJWindow), + (14, 2, obj_shape, ObjectShape, Square, Horizontal, Vertical), + ] + ); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum ObjectRender { + Normal = 0, + Affine = 1, + Disabled = 2, + DoubleAreaAffine = 3, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum ObjectMode { + Normal = 0, + SemiTransparent = 1, + OBJWindow = 2 +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum ObjectShape { + Square = 0, + Horizontal = 1, + Vertical = 2 +} + +newtype! { + // TODO + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] + OBJAttr1, u16 +} +impl OBJAttr1 { + bool_bits!( + u16, + [ + (12, hflip), + (13, vflip), + ] + ); + + multi_bits!( + u16, + [ + (0, 9, x_coordinate), + (9, 5, affine_index), + (14, 2, obj_size, ObjectSize, Zero, One, Two, Three), + ] + ); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum ObjectSize { + Zero = 0, + One = 1, + Two = 2, + Three = 3, +} + +newtype! { + // TODO + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] + OBJAttr2, u16 +} +impl OBJAttr2 { + multi_bits!( + u16, + [ + (0, 10, tile_id), + (10, 2, priority), + (12, 4, palbank), + ] + ); +}