backgrounds and OAM start

This commit is contained in:
Lokathor 2018-12-26 20:19:53 -07:00
parent 01ac3ec09e
commit 6271614335
6 changed files with 324 additions and 53 deletions

View file

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

View file

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

View file

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

View file

@ -2,36 +2,153 @@
use super::*;
// BG0 Control. Read/Write.
pub const BG0CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0008) };
// BG1 Control. Read/Write.
pub const BG1CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000A) };
// BG2 Control. Read/Write.
pub const BG2CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000C) };
// BG3 Control. Read/Write.
pub const BG3CNT: VolAddress<BackgroundControlSetting> = 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<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0008) };
pub const BG1CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000A) };
pub const BG2CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000C) };
pub const BG3CNT: VolAddress<BackgroundControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_0010) };
// BG0 Y-Offset. Write only. Text mode only. 9 bits.
pub const BG0VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0012) };
// BG1 X-Offset. Write only. Text mode only. 9 bits.
pub const BG1HOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0012) };
// BG1 Y-Offset. Write only. Text mode only. 9 bits.
pub const BG1VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0012) };
// BG2 X-Offset. Write only. Text mode only. 9 bits.
pub const BG2HOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0018) };
// BG2 Y-Offset. Write only. Text mode only. 9 bits.
pub const BG2VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_001A) };
// BG3 X-Offset. Write only. Text mode only. 9 bits.
pub const BG3HOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_001C) };
// BG3 Y-Offset. Write only. Text mode only. 9 bits.
pub const BG3VOFS: VolAddress<u16> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_0040) };
// pub const WIN1H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0042) };
// pub const WIN0V: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0044) };
// pub const WIN1V: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0046) };
// pub const WININ: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0048) };
// pub const WINOUT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_004A) };
/// Global mosaic effect control. Write-only.
pub const MOSAIC: VolAddress<MosaicSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_0050) };
// pub const BLDALPHA: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0052) };
// pub const BLDY: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0054) };

View file

@ -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<DisplayControlSetting> = 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<DisplayStatusSetting> = 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<u16> = 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()

100
src/oam.rs Normal file
View file

@ -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),
]
);
}