1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#![cfg(feature = "unsafe_addresses")]

use super::*;

/// A ZST for namespacing VideoMode4 stuff into one place.
///
/// In Mode 4 the majority of VRAM is occupied with two "indexmaps", frame 0 and
/// frame 1, each as large as the GBA's physical display.
///
/// Each position in a frame contains a `u8` index value into the `PALRAM`,
/// which contains the actual color value that will be displayed at that
/// location of the frame. As with most other indexed color on the GBA, index 0
/// is "transparent".
///
/// You can access the address of a location by `row` and then `col` or by `x`
/// and then `y`. It will give equivalent results.
///
/// There is a complication with this. You can't write just a single `u8` to
/// VRAM. If you write a single `u8`, that byte gets written to both the low
/// byte and the high byte of the `u16` that the byte is inside of. In other
/// words, if you write a byte to `rc(0,0)`, you also would write the same value
/// to `rc(0,1)` as well. The only way to edit a _single_ location in Mode4 is
/// to read in the current value of a pair of locations, change just the byte
/// you want to change, and then write back the data.
///
/// ```rust
/// # use rubidium::*;
/// let p = Mode4.frame(1).row(8).col(5);
/// let q = Mode4.frame(1).x(5).y(8);
/// assert_eq!(p, q);
/// ```
///
/// See Also: [`VideoMode`]
#[derive(Debug, Clone, Copy)]
pub struct Mode4;

impl Mode4 {
  /// In Mode4 each frame is 240 wide.
  pub const WIDTH: usize = 240;

  /// In Mode4 each frame is 160 wide.
  pub const HEIGHT: usize = 160;

  /// Selects frame 0 or frame 1.
  /// ## Panics
  /// If the input is greater than 1.
  #[must_use]
  #[inline(always)]
  pub fn frame(self, frame: usize) -> Mode4Frame {
    assert!(frame < 2);
    let addr = VRAM + 0xA000 * frame;
    Mode4Frame { addr }
  }
}

/// One of the two indexmap frames available in Mode 4.
///
/// See [`Mode4`]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct Mode4Frame {
  addr: usize,
}

impl Mode4Frame {
  /// Index by `row` to pick out a row within this frame.
  /// ## Panics
  /// If the index is out of bounds.
  #[must_use]
  #[inline(always)]
  pub fn row(self, row: usize) -> Mode4Row {
    assert!(row < Mode4::HEIGHT);
    let addr = self.addr + (row * Mode4::WIDTH * size_of::<u8>());
    Mode4Row { addr }
  }

  /// Index by `x` to pick out a column within this frame.
  ///
  /// Because of the VRAM limits on writing just a single byte at a time, this
  /// clears the lowest bit returning, so you always get an odd column position.
  ///
  /// ## Panics
  /// If the index is out of bounds.
  #[must_use]
  #[inline(always)]
  pub fn x(self, x: usize) -> Mode4Col {
    assert!(x < Mode4::WIDTH);
    let x = x & (!1_usize);
    let addr = self.addr + (x * size_of::<u8>());
    Mode4Col { addr }
  }
}

/// A row within one of the two Mode 4 indexmaps.
///
/// See [`Mode4`]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct Mode4Row {
  addr: usize,
}

impl Mode4Row {
  /// Offset by `col` to get a location within this row.
  ///
  /// Because of the VRAM limits on writing just a single byte at a time, this
  /// clears the lowest bit returning, so you always get an odd column position.
  /// ## Panics
  /// If the index is out of bounds.
  #[must_use]
  #[inline(always)]
  pub fn col(self, col: usize) -> SimpleVolAddr<u16> {
    assert!(col < Mode4::WIDTH);
    let col = col & (!1_usize);
    unsafe { SimpleVolAddr::new(self.addr + col * size_of::<u8>()) }
  }
}

/// A column within one of the two Mode 4 indexmaps.
///
/// See [`Mode4`]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct Mode4Col {
  addr: usize,
}

impl Mode4Col {
  /// Offset by `y` to get a location within this column.
  /// ## Panics
  /// If the index is out of bounds.
  #[must_use]
  #[inline(always)]
  pub fn y(self, y: usize) -> SimpleVolAddr<u16> {
    assert!(y < Mode4::HEIGHT);
    unsafe {
      SimpleVolAddr::new(self.addr + (y * Mode4::WIDTH * size_of::<u8>()))
    }
  }
}