mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
backgrounds and OAM start
This commit is contained in:
parent
01ac3ec09e
commit
6271614335
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) };
|
||||
|
|
|
@ -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
100
src/oam.rs
Normal 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),
|
||||
]
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue