diff --git a/src/ppu.rs b/src/ppu.rs index 85d74df..739cdcf 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -1,10 +1,16 @@ use crate::Cycle; use crate::GB_HEIGHT; use crate::GB_WIDTH; -use bitfield::bitfield; use std::collections::VecDeque; use std::convert::TryInto; +use registers::{ + BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette, + ObjectPaletteId, ObjectSize, PpuMode, RenderPriority, TileDataAddress, TwoBitsPerPixel, +}; + +mod registers; + const VRAM_SIZE: usize = 0x2000; const OAM_SIZE: usize = 0xA0; const PPU_START_ADDRESS: usize = 0x8000; @@ -61,14 +67,14 @@ impl Ppu { self.cycles += 1; match self.stat.mode() { - Mode::OamScan => { + PpuMode::OamScan => { if self.cycles >= 80.into() { - self.stat.set_mode(Mode::Drawing); + self.stat.set_mode(PpuMode::Drawing); } self.scan_oam(self.cycles.into()); } - Mode::Drawing => { + PpuMode::Drawing => { if self.x_pos >= 160 { if self.stat.hblank_int() { // Enable HBlank LCDStat Interrupt @@ -81,7 +87,7 @@ impl Ppu { self.fetcher.hblank_reset(); self.obj_buffer.clear(); - self.stat.set_mode(Mode::HBlank); + self.stat.set_mode(PpuMode::HBlank); } else if self.control.lcd_enabled() { // Only Draw when the LCD Is Enabled self.draw(self.cycles.into()); @@ -89,7 +95,7 @@ impl Ppu { self.reset(); } } - Mode::HBlank => { + PpuMode::HBlank => { // This mode will always end at 456 cycles if self.cycles >= 456.into() { @@ -114,20 +120,20 @@ impl Ppu { self.int.set_lcd_stat(true); } - Mode::VBlank + PpuMode::VBlank } else { if self.stat.oam_int() { // Enable OAM LCDStat Interrupt self.int.set_lcd_stat(true); } - Mode::OamScan + PpuMode::OamScan }; self.stat.set_mode(next_mode); } } - Mode::VBlank => { + PpuMode::VBlank => { if self.cycles > 456.into() { self.cycles %= 456; self.pos.line_y += 1; @@ -146,7 +152,7 @@ impl Ppu { self.int.set_lcd_stat(true); } - self.stat.set_mode(Mode::OamScan); + self.stat.set_mode(PpuMode::OamScan); } } } @@ -392,7 +398,7 @@ impl Ppu { self.cycles = Cycle::new(0); self.x_pos = 0; - self.stat.set_mode(Mode::OamScan); + self.stat.set_mode(PpuMode::OamScan); self.pos.line_y = 0; self.fetcher.bg.reset(); @@ -462,80 +468,6 @@ impl Interrupt { } } -bitfield! { - pub struct LCDStatus(u8); - impl Debug; - pub coincidence_int, set_coincidence_int: 6; - pub oam_int, set_oam_int: 5; - pub vblank_int, set_vblank_int: 4; - pub hblank_int, set_hblank_int: 3; - pub coincidence, set_coincidence: 2; // LYC == LY Flag - from into Mode, _mode, set_mode: 1, 0; -} - -impl LCDStatus { - pub fn mode(&self) -> Mode { - self._mode() - } -} - -impl Copy for LCDStatus {} -impl Clone for LCDStatus { - fn clone(&self) -> Self { - *self - } -} - -impl Default for LCDStatus { - fn default() -> Self { - Self(0x80) // bit 7 is always 1 - } -} - -impl From for LCDStatus { - fn from(byte: u8) -> Self { - Self(byte) - } -} - -impl From for u8 { - fn from(status: LCDStatus) -> Self { - status.0 - } -} - -#[derive(Debug, Clone, Copy)] -pub enum Mode { - HBlank = 0, - VBlank = 1, - OamScan = 2, - Drawing = 3, -} - -impl From for Mode { - fn from(byte: u8) -> Self { - match byte { - 0b00 => Self::HBlank, - 0b01 => Self::VBlank, - 0b10 => Self::OamScan, - 0b11 => Self::Drawing, - _ => unreachable!("{:#04X} is not a valid value for LCDMode", byte), - } - } -} - -impl From for u8 { - fn from(mode: Mode) -> Self { - mode as Self - } -} - -impl Default for Mode { - fn default() -> Self { - Self::HBlank - } -} - #[derive(Debug, Clone, Copy, Default)] pub struct ScreenPosition { pub scroll_y: u8, @@ -546,194 +478,6 @@ pub struct ScreenPosition { pub window_x: u8, } -bitfield! { - pub struct LCDControl(u8); - impl Debug; - lcd_enabled, set_lcd_enabled: 7; - from into TileMapAddress, win_tile_map_addr, set_win_tile_map_addr: 6, 6; - window_enabled, set_window_enabled: 5; - from into TileDataAddress, tile_data_addr, set_tile_data_addr: 4, 4; - from into TileMapAddress, bg_tile_map_addr, set_bg_tile_map_addr: 3, 3; - from into ObjectSize, obj_size, set_obj_size: 2, 2; - obj_enabled, set_obj_enabled: 1; - bg_win_enabled, set_bg_win_enabled: 0; -} - -impl Copy for LCDControl {} -impl Clone for LCDControl { - fn clone(&self) -> Self { - *self - } -} - -impl Default for LCDControl { - fn default() -> Self { - Self(0) - } -} - -impl From for LCDControl { - fn from(byte: u8) -> Self { - Self(byte) - } -} - -impl From for u8 { - fn from(ctrl: LCDControl) -> Self { - ctrl.0 - } -} - -#[derive(Debug, Clone, Copy)] -enum TileMapAddress { - X9800 = 0, - X9C00 = 1, -} - -impl TileMapAddress { - pub fn into_address(self) -> u16 { - match self { - TileMapAddress::X9800 => 0x9800, - TileMapAddress::X9C00 => 0x9C00, - } - } -} - -impl From for TileMapAddress { - fn from(byte: u8) -> Self { - match byte { - 0b00 => Self::X9800, - 0b01 => Self::X9C00, - _ => unreachable!("{:#04X} is not a valid value for TileMapRegister", byte), - } - } -} - -impl From for u8 { - fn from(reg: TileMapAddress) -> Self { - reg as Self - } -} - -impl Default for TileMapAddress { - fn default() -> Self { - Self::X9800 - } -} - -#[derive(Debug, Clone, Copy)] -enum TileDataAddress { - X8800 = 0, - X8000 = 1, -} - -impl From for TileDataAddress { - fn from(byte: u8) -> Self { - match byte { - 0b00 => Self::X8800, - 0b01 => Self::X8000, - _ => unreachable!("{:#04X} is not a valid value for TileDataRegister", byte), - } - } -} - -impl From for u8 { - fn from(reg: TileDataAddress) -> Self { - reg as Self - } -} - -impl Default for TileDataAddress { - fn default() -> Self { - Self::X8800 - } -} - -#[derive(Debug, Clone, Copy)] -enum ObjectSize { - Eight = 0, - Sixteen = 1, -} - -impl From for ObjectSize { - fn from(byte: u8) -> Self { - match byte { - 0b00 => Self::Eight, - 0b01 => Self::Sixteen, - _ => unreachable!("{:#04X} is not a valid value for ObjSize", byte), - } - } -} - -impl From for u8 { - fn from(size: ObjectSize) -> Self { - size as Self - } -} - -impl Default for ObjectSize { - fn default() -> Self { - Self::Eight - } -} - -#[derive(Debug, Clone, Copy)] -pub enum GrayShade { - White = 0, - LightGray = 1, - DarkGray = 2, - Black = 3, -} - -impl GrayShade { - pub fn into_rgba(self) -> [u8; 4] { - match self { - GrayShade::White => WHITE, - GrayShade::LightGray => LIGHT_GRAY, - GrayShade::DarkGray => DARK_GRAY, - GrayShade::Black => BLACK, - } - } - - pub fn from_rgba(slice: &[u8]) -> Self { - let rgba: [u8; 4] = slice - .try_into() - .expect("Unable to interpret &[u8] as [u8; 4]"); - - match rgba { - WHITE => GrayShade::White, - LIGHT_GRAY => GrayShade::LightGray, - DARK_GRAY => GrayShade::DarkGray, - BLACK => GrayShade::Black, - _ => panic!("{:#04X?} is not a colour the DMG-01 supports", rgba), - } - } -} - -impl Default for GrayShade { - fn default() -> Self { - Self::White - } -} - -impl From for GrayShade { - fn from(byte: u8) -> Self { - match byte { - 0b00 => GrayShade::White, - 0b01 => GrayShade::LightGray, - 0b10 => GrayShade::DarkGray, - 0b11 => GrayShade::Black, - _ => unreachable!("{:#04X} is not a valid value for GrayShade", byte), - } - } -} - -impl From for u8 { - fn from(shade: GrayShade) -> Self { - shade as Self - } -} - #[derive(Debug, Clone, Copy, Default)] pub struct Monochrome { pub bg_palette: BackgroundPalette, @@ -741,112 +485,6 @@ pub struct Monochrome { pub obj_palette_1: ObjectPalette, } -bitfield! { - pub struct BackgroundPalette(u8); - impl Debug; - pub from into GrayShade, i3_colour, set_i3_colour: 7, 6; - pub from into GrayShade, i2_colour, set_i2_colour: 5, 4; - pub from into GrayShade, i1_colour, set_i1_colour: 3, 2; - pub from into GrayShade, i0_colour, set_i0_colour: 1, 0; -} - -impl BackgroundPalette { - pub fn colour(&self, id: u8) -> GrayShade { - match id { - 0b00 => self.i0_colour(), - 0b01 => self.i1_colour(), - 0b10 => self.i2_colour(), - 0b11 => self.i3_colour(), - _ => unreachable!("{:#04X} is not a valid BG colour id", id), - } - } -} - -impl Copy for BackgroundPalette {} -impl Clone for BackgroundPalette { - fn clone(&self) -> Self { - *self - } -} - -impl Default for BackgroundPalette { - fn default() -> Self { - Self(0) - } -} - -impl From for BackgroundPalette { - fn from(byte: u8) -> Self { - Self(byte) - } -} - -impl From for u8 { - fn from(palette: BackgroundPalette) -> Self { - palette.0 - } -} - -bitfield! { - pub struct ObjectPalette(u8); - impl Debug; - pub from into GrayShade, i3_colour, set_i3_colour: 7, 6; - pub from into GrayShade, i2_colour, set_i2_colour: 5, 4; - pub from into GrayShade, i1_colour, set_i1_colour: 3, 2; -} - -impl ObjectPalette { - pub fn colour(&self, id: u8) -> Option { - match id { - 0b00 => None, - 0b01 => Some(self.i1_colour()), - 0b10 => Some(self.i2_colour()), - 0b11 => Some(self.i3_colour()), - _ => unreachable!("{:#04X} is not a valid OBJ colour id", id), - } - } -} - -impl Copy for ObjectPalette {} -impl Clone for ObjectPalette { - fn clone(&self) -> Self { - *self - } -} - -impl Default for ObjectPalette { - fn default() -> Self { - Self(0) - } -} - -impl From for ObjectPalette { - fn from(byte: u8) -> Self { - Self(byte) - } -} - -impl From for u8 { - fn from(palette: ObjectPalette) -> Self { - palette.0 - } -} - -struct TwoBitsPerPixel(u8, u8); - -impl TwoBitsPerPixel { - pub fn from_bytes(higher: u8, lower: u8) -> Self { - Self(higher, lower) - } - - pub fn pixel(&self, bit: usize) -> u8 { - let higher = self.0 >> bit; - let lower = self.1 >> bit; - - (higher & 0x01) << 1 | lower & 0x01 - } -} - #[derive(Debug, Clone)] pub struct ObjectAttributeTable { buf: Box<[u8; OAM_SIZE]>, @@ -910,98 +548,6 @@ impl<'a> From<&'a [u8; 4]> for ObjectAttribute { } } -bitfield! { - pub struct ObjectFlags(u8); - impl Debug; - - from into RenderPriority, priority, set_priority: 7, 7; - y_flip, set_y_flip: 6; - x_flip, set_x_flip: 5; - from into ObjectPaletteId, palette, set_palette: 4, 4; -} - -impl Eq for ObjectFlags {} -impl PartialEq for ObjectFlags { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Copy for ObjectFlags {} -impl Clone for ObjectFlags { - fn clone(&self) -> Self { - *self - } -} - -impl From for ObjectFlags { - fn from(byte: u8) -> Self { - Self(byte) - } -} - -impl From for u8 { - fn from(flags: ObjectFlags) -> Self { - flags.0 - } -} - -impl Default for ObjectFlags { - fn default() -> Self { - Self(0) - } -} - -#[derive(Debug, Clone, Copy)] -pub enum RenderPriority { - Object = 0, - BackgroundAndWindow = 1, -} - -impl From for RenderPriority { - fn from(byte: u8) -> Self { - match byte { - 0b00 => Self::Object, - 0b01 => Self::BackgroundAndWindow, - _ => unreachable!("{:#04X} is not a valid value for RenderPriority", byte), - } - } -} - -impl From for u8 { - fn from(priority: RenderPriority) -> Self { - priority as u8 - } -} - -impl Default for RenderPriority { - fn default() -> Self { - Self::Object - } -} - -#[derive(Debug, Clone, Copy)] -pub enum ObjectPaletteId { - Zero = 0, - One = 1, -} - -impl From for ObjectPaletteId { - fn from(byte: u8) -> Self { - match byte { - 0b00 => ObjectPaletteId::Zero, - 0b01 => ObjectPaletteId::One, - _ => unreachable!("{:#04X} is not a valid value for BgPaletteNumber", byte), - } - } -} - -impl From for u8 { - fn from(palette_num: ObjectPaletteId) -> Self { - palette_num as u8 - } -} - #[derive(Debug, Clone, Copy)] struct ObjectBuffer { buf: [Option; OBJECT_LIMIT], diff --git a/src/ppu/registers.rs b/src/ppu/registers.rs new file mode 100644 index 0000000..fa3e8ea --- /dev/null +++ b/src/ppu/registers.rs @@ -0,0 +1,457 @@ +use super::{BLACK, DARK_GRAY, LIGHT_GRAY, WHITE}; +use bitfield::bitfield; +use std::convert::TryInto; + +bitfield! { + pub struct LCDStatus(u8); + impl Debug; + pub coincidence_int, set_coincidence_int: 6; + pub oam_int, set_oam_int: 5; + pub vblank_int, set_vblank_int: 4; + pub hblank_int, set_hblank_int: 3; + pub coincidence, set_coincidence: 2; // LYC == LY Flag + pub from into PpuMode, mode, set_mode: 1, 0; +} + +impl Copy for LCDStatus {} +impl Clone for LCDStatus { + fn clone(&self) -> Self { + *self + } +} + +impl Default for LCDStatus { + fn default() -> Self { + Self(0x80) // bit 7 is always 1 + } +} + +impl From for LCDStatus { + fn from(byte: u8) -> Self { + Self(byte) + } +} + +impl From for u8 { + fn from(status: LCDStatus) -> Self { + status.0 + } +} + +#[derive(Debug, Clone, Copy)] +pub enum PpuMode { + HBlank = 0, + VBlank = 1, + OamScan = 2, + Drawing = 3, +} + +impl From for PpuMode { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::HBlank, + 0b01 => Self::VBlank, + 0b10 => Self::OamScan, + 0b11 => Self::Drawing, + _ => unreachable!("{:#04X} is not a valid value for LCDMode", byte), + } + } +} + +impl From for u8 { + fn from(mode: PpuMode) -> Self { + mode as Self + } +} + +impl Default for PpuMode { + fn default() -> Self { + Self::HBlank + } +} + +bitfield! { + pub struct LCDControl(u8); + impl Debug; + pub lcd_enabled, set_lcd_enabled: 7; + pub from into TileMapAddress, win_tile_map_addr, set_win_tile_map_addr: 6, 6; + pub window_enabled, set_window_enabled: 5; + pub from into TileDataAddress, tile_data_addr, set_tile_data_addr: 4, 4; + pub from into TileMapAddress, bg_tile_map_addr, set_bg_tile_map_addr: 3, 3; + pub from into ObjectSize, obj_size, set_obj_size: 2, 2; + obj_enabled, set_obj_enabled: 1; + bg_win_enabled, set_bg_win_enabled: 0; +} + +impl Copy for LCDControl {} +impl Clone for LCDControl { + fn clone(&self) -> Self { + *self + } +} + +impl Default for LCDControl { + fn default() -> Self { + Self(0) + } +} + +impl From for LCDControl { + fn from(byte: u8) -> Self { + Self(byte) + } +} + +impl From for u8 { + fn from(ctrl: LCDControl) -> Self { + ctrl.0 + } +} + +#[derive(Debug, Clone, Copy)] +pub enum TileMapAddress { + X9800 = 0, + X9C00 = 1, +} + +impl TileMapAddress { + pub fn into_address(self) -> u16 { + match self { + TileMapAddress::X9800 => 0x9800, + TileMapAddress::X9C00 => 0x9C00, + } + } +} + +impl From for TileMapAddress { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::X9800, + 0b01 => Self::X9C00, + _ => unreachable!("{:#04X} is not a valid value for TileMapRegister", byte), + } + } +} + +impl From for u8 { + fn from(reg: TileMapAddress) -> Self { + reg as Self + } +} + +impl Default for TileMapAddress { + fn default() -> Self { + Self::X9800 + } +} + +#[derive(Debug, Clone, Copy)] +pub enum TileDataAddress { + X8800 = 0, + X8000 = 1, +} + +impl From for TileDataAddress { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::X8800, + 0b01 => Self::X8000, + _ => unreachable!("{:#04X} is not a valid value for TileDataRegister", byte), + } + } +} + +impl From for u8 { + fn from(reg: TileDataAddress) -> Self { + reg as Self + } +} + +impl Default for TileDataAddress { + fn default() -> Self { + Self::X8800 + } +} + +#[derive(Debug, Clone, Copy)] +pub enum ObjectSize { + Eight = 0, + Sixteen = 1, +} + +impl From for ObjectSize { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::Eight, + 0b01 => Self::Sixteen, + _ => unreachable!("{:#04X} is not a valid value for ObjSize", byte), + } + } +} + +impl From for u8 { + fn from(size: ObjectSize) -> Self { + size as Self + } +} + +impl Default for ObjectSize { + fn default() -> Self { + Self::Eight + } +} + +bitfield! { + pub struct BackgroundPalette(u8); + impl Debug; + pub from into GrayShade, i3_colour, set_i3_colour: 7, 6; + pub from into GrayShade, i2_colour, set_i2_colour: 5, 4; + pub from into GrayShade, i1_colour, set_i1_colour: 3, 2; + pub from into GrayShade, i0_colour, set_i0_colour: 1, 0; +} + +impl BackgroundPalette { + pub fn colour(&self, id: u8) -> GrayShade { + match id { + 0b00 => self.i0_colour(), + 0b01 => self.i1_colour(), + 0b10 => self.i2_colour(), + 0b11 => self.i3_colour(), + _ => unreachable!("{:#04X} is not a valid BG colour id", id), + } + } +} + +impl Copy for BackgroundPalette {} +impl Clone for BackgroundPalette { + fn clone(&self) -> Self { + *self + } +} + +impl Default for BackgroundPalette { + fn default() -> Self { + Self(0) + } +} + +impl From for BackgroundPalette { + fn from(byte: u8) -> Self { + Self(byte) + } +} + +impl From for u8 { + fn from(palette: BackgroundPalette) -> Self { + palette.0 + } +} + +bitfield! { + pub struct ObjectPalette(u8); + impl Debug; + pub from into GrayShade, i3_colour, set_i3_colour: 7, 6; + pub from into GrayShade, i2_colour, set_i2_colour: 5, 4; + pub from into GrayShade, i1_colour, set_i1_colour: 3, 2; +} + +impl ObjectPalette { + pub fn colour(&self, id: u8) -> Option { + match id { + 0b00 => None, + 0b01 => Some(self.i1_colour()), + 0b10 => Some(self.i2_colour()), + 0b11 => Some(self.i3_colour()), + _ => unreachable!("{:#04X} is not a valid OBJ colour id", id), + } + } +} + +impl Copy for ObjectPalette {} +impl Clone for ObjectPalette { + fn clone(&self) -> Self { + *self + } +} + +impl Default for ObjectPalette { + fn default() -> Self { + Self(0) + } +} + +impl From for ObjectPalette { + fn from(byte: u8) -> Self { + Self(byte) + } +} + +impl From for u8 { + fn from(palette: ObjectPalette) -> Self { + palette.0 + } +} + +pub struct TwoBitsPerPixel(u8, u8); + +impl TwoBitsPerPixel { + pub fn from_bytes(higher: u8, lower: u8) -> Self { + Self(higher, lower) + } + + pub fn pixel(&self, bit: usize) -> u8 { + let higher = self.0 >> bit; + let lower = self.1 >> bit; + + (higher & 0x01) << 1 | lower & 0x01 + } +} + +bitfield! { + pub struct ObjectFlags(u8); + impl Debug; + + pub from into RenderPriority, priority, set_priority: 7, 7; + pub y_flip, set_y_flip: 6; + pub x_flip, set_x_flip: 5; + pub from into ObjectPaletteId, palette, set_palette: 4, 4; +} + +impl Eq for ObjectFlags {} +impl PartialEq for ObjectFlags { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Copy for ObjectFlags {} +impl Clone for ObjectFlags { + fn clone(&self) -> Self { + *self + } +} + +impl From for ObjectFlags { + fn from(byte: u8) -> Self { + Self(byte) + } +} + +impl From for u8 { + fn from(flags: ObjectFlags) -> Self { + flags.0 + } +} + +impl Default for ObjectFlags { + fn default() -> Self { + Self(0) + } +} + +#[derive(Debug, Clone, Copy)] +pub enum ObjectPaletteId { + Zero = 0, + One = 1, +} + +impl From for ObjectPaletteId { + fn from(byte: u8) -> Self { + match byte { + 0b00 => ObjectPaletteId::Zero, + 0b01 => ObjectPaletteId::One, + _ => unreachable!("{:#04X} is not a valid value for BgPaletteNumber", byte), + } + } +} + +impl From for u8 { + fn from(palette_num: ObjectPaletteId) -> Self { + palette_num as u8 + } +} + +#[derive(Debug, Clone, Copy)] +pub enum RenderPriority { + Object = 0, + BackgroundAndWindow = 1, +} + +impl From for RenderPriority { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::Object, + 0b01 => Self::BackgroundAndWindow, + _ => unreachable!("{:#04X} is not a valid value for RenderPriority", byte), + } + } +} + +impl From for u8 { + fn from(priority: RenderPriority) -> Self { + priority as u8 + } +} + +impl Default for RenderPriority { + fn default() -> Self { + Self::Object + } +} + +#[derive(Debug, Clone, Copy)] +pub enum GrayShade { + White = 0, + LightGray = 1, + DarkGray = 2, + Black = 3, +} + +impl GrayShade { + pub fn into_rgba(self) -> [u8; 4] { + match self { + GrayShade::White => WHITE, + GrayShade::LightGray => LIGHT_GRAY, + GrayShade::DarkGray => DARK_GRAY, + GrayShade::Black => BLACK, + } + } + + pub fn from_rgba(slice: &[u8]) -> Self { + let rgba: [u8; 4] = slice + .try_into() + .expect("Unable to interpret &[u8] as [u8; 4]"); + + match rgba { + WHITE => GrayShade::White, + LIGHT_GRAY => GrayShade::LightGray, + DARK_GRAY => GrayShade::DarkGray, + BLACK => GrayShade::Black, + _ => panic!("{:#04X?} is not a colour the DMG-01 supports", rgba), + } + } +} + +impl Default for GrayShade { + fn default() -> Self { + Self::White + } +} + +impl From for GrayShade { + fn from(byte: u8) -> Self { + match byte { + 0b00 => GrayShade::White, + 0b01 => GrayShade::LightGray, + 0b10 => GrayShade::DarkGray, + 0b11 => GrayShade::Black, + _ => unreachable!("{:#04X} is not a valid value for GrayShade", byte), + } + } +} + +impl From for u8 { + fn from(shade: GrayShade) -> Self { + shade as Self + } +}