diff options
Diffstat (limited to 'src/devices/screen.rs')
-rw-r--r-- | src/devices/screen.rs | 258 |
1 files changed, 0 insertions, 258 deletions
diff --git a/src/devices/screen.rs b/src/devices/screen.rs deleted file mode 100644 index a61d8b3..0000000 --- a/src/devices/screen.rs +++ /dev/null @@ -1,258 +0,0 @@ -mod sprite_data; -mod draw_line; -mod draw_rect; -mod draw_sprite; - -pub use sprite_data::*; -use geometry::HasDimensions; -use phosphor::*; -use std::cmp::{min, max, Ordering}; -use std::iter::zip; - -pub type ScreenDimensions = geometry::Dimensions<u16>; -pub type ScreenPosition = geometry::Point<u16>; -pub type Plane = [u8; 8]; -pub type Sprite = [[u8; 8]; 8]; - -const TRANSPARENT: u8 = 0x08; -const FLIP_DIAGONAL: u8 = 0x04; -const FLIP_VERTICAL: u8 = 0x02; -const FLIP_HORIZONTAL: u8 = 0x01; -const NEGATIVE: u8 = 0x80; -const VERTICAL: u8 = 0x40; - - -#[derive(Copy, Clone)] -pub enum ScreenLayer { Background, Foreground } - -macro_rules! test { ($value:expr, $mask:expr) => { $value & $mask != 0 }; } - - -pub struct ScreenDevice { - pub wake_flag: bool, - - /// Each byte represents a screen pixel, left-to-right and top-to-bottom. - // Only the bottom four bits of each byte are used. - pub foreground: Vec<u8>, - pub background: Vec<u8>, - pub dirty: bool, - pub resizable: bool, - - pub cursor: ScreenPosition, - pub vector: ScreenPosition, - pub dimensions: ScreenDimensions, - - pub palette_high: u8, - pub palette: [Colour; 16], - pub sprite_buffer: SpriteBuffer, -} - -impl ScreenDevice { - pub fn new() -> Self { - Self { - wake_flag: false, - - foreground: Vec::new(), - background: Vec::new(), - dirty: false, - resizable: true, - - cursor: ScreenPosition::ZERO, - vector: ScreenPosition::ZERO, - dimensions: ScreenDimensions::ZERO, - - palette_high: 0, - palette: [Colour::BLACK; 16], - sprite_buffer: SpriteBuffer::new(), - } - } - - pub fn set_size(&mut self) { - self.resizable = false; - self.resize(self.dimensions); - } - - // Resize the screen buffers while preserving the current content. - pub fn resize(&mut self, dimensions: ScreenDimensions) { - let old_width = self.dimensions.width as usize; - let old_height = self.dimensions.height as usize; - let new_width = dimensions.width as usize; - let new_height = dimensions.height as usize; - let new_area = dimensions.area_usize(); - let y_range = 0..min(old_height, new_height); - - match new_width.cmp(&old_width) { - Ordering::Less => { - for y in y_range { - let from = y * old_width; - let to = y * new_width; - let len = new_width; - self.foreground.copy_within(from..from+len, to); - self.background.copy_within(from..from+len, to); - } - self.foreground.resize(new_area, 0); - self.background.resize(new_area, 0); - }, - Ordering::Greater => { - self.foreground.resize(new_area, 0); - self.background.resize(new_area, 0); - for y in y_range.rev() { - let from = y * old_width; - let to = y * new_width; - let len = old_width; - self.foreground.copy_within(from..from+len, to); - self.background.copy_within(from..from+len, to); - self.foreground[to+len..to+new_width].fill(0); - self.background[to+len..to+new_width].fill(0); - } - }, - Ordering::Equal => { - self.foreground.resize(new_area, 0); - self.background.resize(new_area, 0); - }, - }; - - self.dimensions = dimensions; - self.dirty = true; - self.wake_flag = true; - } - - pub fn render(&mut self, buffer: &mut Buffer) { - // Pre-calculate a lookup table for the colour palette - let mut lookup = [Colour::BLACK; 256]; - for (i, c) in lookup.iter_mut().enumerate() { - match i > 0x0f { - true => *c = self.palette[i >> 4], - false => *c = self.palette[i & 0x0f], - } - }; - // Prepare values - let b_width = buffer.width() as usize; - let b_height = buffer.height() as usize; - let s_width = self.dimensions.width() as usize; - let s_height = self.dimensions.height() as usize; - - // Write colours to the buffer - if b_width == s_width && b_height == s_height { - let screen_iter = zip(&self.background, &self.foreground); - let buffer_iter = buffer.as_mut_slice(); - for (b, (bg, fg)) in zip(buffer_iter, screen_iter) { - *b = lookup[(fg << 4 | bg) as usize]; - } - // Write colours to the buffer when the size of the buffer is wrong - } else { - let width = min(b_width, s_width); - let height = min(b_height, s_height); - let width_excess = b_width.saturating_sub(width); - let b_slice = &mut buffer.as_mut_slice(); - let mut bi = 0; - let mut si = 0; - for _ in 0..height { - let b_iter = &mut b_slice[bi..bi+width]; - let s_iter = zip( - &self.background[si..si+width], - &self.foreground[si..si+width], - ); - for (b, (bg, fg)) in zip(b_iter, s_iter) { - *b = lookup[(fg << 4 | bg) as usize]; - } - b_slice[bi+width..bi+width+width_excess].fill(lookup[0]); - bi += b_width; - si += s_width; - } - b_slice[bi..].fill(lookup[0]); - } - self.dirty = false; - } - - pub fn set_palette_high(&mut self, val: u8) { - self.palette_high = val; - } - - pub fn set_palette_low(&mut self, val: u8) { - let index = (self.palette_high >> 4) as usize; - let red = (self.palette_high & 0x0f) * 17; - let green = (val >> 4) * 17; - let blue = (val & 0x0f) * 17; - self.palette[index] = Colour::from_rgb(red, green, blue); - self.dirty = true; - } - - pub fn shunt(&mut self, val: u8) { - let negative = test!(val, NEGATIVE); - let vertical = test!(val, VERTICAL); - let dist = (val & 0x3f) as u16; - match (negative, vertical) { - (false, false) => self.cursor.x = self.cursor.x.wrapping_add(dist), - (false, true) => self.cursor.y = self.cursor.y.wrapping_add(dist), - ( true, false) => self.cursor.x = self.cursor.x.wrapping_sub(dist), - ( true, true) => self.cursor.y = self.cursor.y.wrapping_sub(dist), - }; - } - - pub fn draw(&mut self, val: u8) { - let operation = val & 0x70; - let parameters = val & 0x0f; - let layer = match val & 0x80 != 0 { - true => ScreenLayer::Foreground, - false => ScreenLayer::Background - }; - match operation { - 0x00 => self.draw_pixel(parameters, layer, self.cursor), - 0x10 => self.draw_sprite_1bit(parameters, layer), - 0x20 => self.fill_layer(parameters, layer), - 0x30 => self.draw_sprite_2bit(parameters, layer), - 0x40 => self.draw_line(parameters, layer), - 0x50 => self.draw_line_1bit(parameters, layer), - 0x60 => self.draw_rect(parameters, layer), - 0x70 => self.draw_rect_1bit(parameters, layer), - _ => unreachable!(), - }; - - self.dirty = true; - self.vector = self.cursor; - } - - // Draw a single pixel of a single colour - fn draw_pixel(&mut self, colour: u8, layer: ScreenLayer, point: ScreenPosition) { - let dim = self.dimensions; - if !dim.contains_point(point) || colour > 0xf { return } - let index = point.x as usize + ((dim.width as usize) * (point.y as usize)); - match layer { - ScreenLayer::Background => self.background[index] = colour, - ScreenLayer::Foreground => self.foreground[index] = colour, - }; - } - - // Fill an entire screen layer with a single colour - fn fill_layer(&mut self, colour: u8, layer: ScreenLayer) { - match layer { - ScreenLayer::Background => self.background.fill(colour), - ScreenLayer::Foreground => self.foreground.fill(colour), - } - } - - /// Returns [x0, y0, x1, y1], ensuring that x0 <= x1 and y0 <= y1 - fn find_vector_bounding_box(&self) -> Option<[usize; 4]> { - macro_rules! raise {($v:expr) => {$v.wrapping_add(0x8000)};} - macro_rules! lower {($v:expr) => {$v.wrapping_sub(0x8000)};} - - let [p0, p1] = [self.cursor, self.vector]; - let [p0x, p0y] = [ raise!(p0.x), raise!(p0.y) ]; - let [p1x, p1y] = [ raise!(p1.x), raise!(p1.y) ]; - let [x0, y0] = [ min(p0x, p1x), min(p0y, p1y) ]; - let [x1, y1] = [ max(p0x, p1x), max(p0y, p1y) ]; - let right = self.dimensions.width.saturating_sub(1); - let bottom = self.dimensions.height.saturating_sub(1); - if x0 > raise!(right) || y0 > raise!(bottom) || x1 < 0x8000 || y1 < 0x8000 { - None - } else { - Some([ - if x0 < 0x8000 { 0 } else { min(lower!(x0), right) } as usize, - if y0 < 0x8000 { 0 } else { min(lower!(y0), bottom) } as usize, - if x1 < 0x8000 { 0 } else { min(lower!(x1), right) } as usize, - if y1 < 0x8000 { 0 } else { min(lower!(y1), bottom) } as usize, - ]) - } - } -} |