summaryrefslogtreecommitdiff
path: root/src/devices/screen
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/screen')
-rw-r--r--src/devices/screen/draw_line.rs223
-rw-r--r--src/devices/screen/draw_rect.rs42
-rw-r--r--src/devices/screen/draw_sprite.rs25
-rw-r--r--src/devices/screen/sprite_data.rs98
-rw-r--r--src/devices/screen/vector_points.rs21
5 files changed, 377 insertions, 32 deletions
diff --git a/src/devices/screen/draw_line.rs b/src/devices/screen/draw_line.rs
new file mode 100644
index 0000000..94066f4
--- /dev/null
+++ b/src/devices/screen/draw_line.rs
@@ -0,0 +1,223 @@
+use super::*;
+
+impl ScreenDevice {
+ pub fn draw_line(&mut self, colour: u8, layer: ScreenLayer) {
+ let [p0, p1] = [self.cursor, self.vector];
+ match (p0.x == p1.x, p0.y == p1.y) {
+ (false, false) => self.draw_diagonal_line(colour, layer),
+ (false, true) => self.draw_horizontal_line(colour, layer),
+ ( true, false) => self.draw_vertical_line(colour, layer),
+ ( true, true) => self.draw_pixel(colour, layer, p0),
+ };
+ }
+
+ pub fn draw_line_1bit(&mut self, params: u8, layer: ScreenLayer) {
+ let [p0, p1] = [self.cursor, self.vector];
+ match (p0.x == p1.x, p0.y == p1.y) {
+ (false, false) => self.draw_diagonal_line_1bit(params, layer),
+ (false, true) => self.draw_horizontal_line_1bit(params, layer),
+ ( true, false) => self.draw_vertical_line_1bit(params, layer),
+ ( true, true) => self.draw_pixel_1bit(params, layer, p0),
+ };
+ }
+
+ pub fn draw_pixel_1bit(&mut self, params: u8, layer: ScreenLayer, point: ScreenPosition) {
+ let dim = self.dimensions;
+ let sprite = self.sprite_buffer.get_1bit_sprite(params);
+ let colour = sprite[point.y as usize % 8][point.x as usize % 8];
+ if !dim.contains_point(point) || colour == 0xff { 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,
+ };
+ }
+
+ fn draw_horizontal_line(&mut self, colour: u8, layer: ScreenLayer) {
+ if let Some([x0, y, x1, _]) = self.find_vector_bounding_box() {
+ let screen_width = self.dimensions.width as usize;
+ let i = screen_width * y;
+ let buffer = match layer {
+ ScreenLayer::Background => &mut self.background,
+ ScreenLayer::Foreground => &mut self.foreground,
+ };
+ buffer[i+x0..=i+x1].fill(colour);
+ }
+ }
+
+ fn draw_horizontal_line_1bit(&mut self, params: u8, layer: ScreenLayer) {
+ if let Some([x0, y, x1, _]) = self.find_vector_bounding_box() {
+ let screen_width = self.dimensions.width as usize;
+ let i = screen_width * y;
+ let buffer = match layer {
+ ScreenLayer::Background => &mut self.background,
+ ScreenLayer::Foreground => &mut self.foreground,
+ };
+ let sprite = self.sprite_buffer.get_1bit_sprite(params);
+ let row = sprite[y % 8];
+ for x in x0..=x1 {
+ let colour = row[x % 8];
+ if colour != 0xff { buffer[i+x] = colour };
+ }
+ }
+ }
+
+ fn draw_vertical_line(&mut self, colour: u8, layer: ScreenLayer) {
+ if let Some([x, y0, _, y1]) = self.find_vector_bounding_box() {
+ let screen_width = self.dimensions.width as usize;
+ let mut i = (screen_width * y0) + x;
+ let buffer = match layer {
+ ScreenLayer::Background => &mut self.background,
+ ScreenLayer::Foreground => &mut self.foreground,
+ };
+ for _ in y0..=y1 {
+ buffer[i] = colour;
+ i += screen_width;
+ }
+ }
+ }
+
+ fn draw_vertical_line_1bit(&mut self, params: u8, layer: ScreenLayer) {
+ if let Some([x, y0, _, y1]) = self.find_vector_bounding_box() {
+ let screen_width = self.dimensions.width as usize;
+ let mut i = (screen_width * y0) + x;
+ let buffer = match layer {
+ ScreenLayer::Background => &mut self.background,
+ ScreenLayer::Foreground => &mut self.foreground,
+ };
+ let sprite = self.sprite_buffer.get_1bit_sprite(params);
+ let mut column = [0u8; 8];
+ for y in 0..8 { column[y] = sprite[y][x % 8] }
+ for y in y0..=y1 {
+ let colour = column[y % 8];
+ if colour != 0xff { buffer[i] = colour };
+ i += screen_width;
+ }
+ }
+ }
+
+ fn draw_diagonal_line(&mut self, colour: u8, layer: ScreenLayer) {
+ fn abs_diff(v0: u16, v1: u16) -> u16 {
+ let v = v1.wrapping_sub(v0);
+ if v > 0x8000 { !v + 1 } else { v }
+ }
+ let [p0, p1] = [self.cursor, self.vector];
+
+ // If the slope of the line is greater than 1.
+ if abs_diff(p0.y, p1.y) > abs_diff(p0.x, p1.x) {
+ // Swap points 0 and 1 such that y0 is always smaller than y1.
+ let (x0, y0, x1, y1) = match p0.y > p1.y {
+ true => (p1.x, p1.y, p0.x, p0.y),
+ false => (p0.x, p0.y, p1.x, p1.y),
+ };
+ let dy = y1 - y0;
+ let (dx, xi) = match x0 > x1 {
+ true => (x0 - x1, 0xffff),
+ false => (x1 - x0, 0x0001),
+ };
+ let dxdy2 = (dx.wrapping_sub(dy)).wrapping_mul(2);
+ let dx2 = dx * 2;
+ let mut d = dx2.wrapping_sub(dy);
+ let mut x = x0;
+
+ for y in y0..=y1 {
+ self.draw_pixel(colour, layer, ScreenPosition::new(x, y));
+ if d < 0x8000 {
+ x = x.wrapping_add(xi); d = d.wrapping_add(dxdy2);
+ } else {
+ d = d.wrapping_add(dx2);
+ }
+ }
+ // If the slope of the line is less than or equal to 1.
+ } else {
+ // Swap points 0 and 1 so that x0 is always smaller than x1.
+ let (x0, y0, x1, y1) = match p0.x > p1.x {
+ true => (p1.x, p1.y, p0.x, p0.y),
+ false => (p0.x, p0.y, p1.x, p1.y),
+ };
+ let dx = x1 - x0;
+ let (dy, yi) = match y0 > y1 {
+ true => (y0 - y1, 0xffff),
+ false => (y1 - y0, 0x0001),
+ };
+ let dydx2 = (dy.wrapping_sub(dx)).wrapping_mul(2);
+ let dy2 = dy * 2;
+ let mut d = dy2.wrapping_sub(dx);
+ let mut y = y0;
+
+ for x in x0..=x1 {
+ self.draw_pixel(colour, layer, ScreenPosition::new(x, y));
+ if d < 0x8000 {
+ y = y.wrapping_add(yi);
+ d = d.wrapping_add(dydx2);
+ } else {
+ d = d.wrapping_add(dy2);
+ }
+ }
+ }
+ }
+
+ fn draw_diagonal_line_1bit(&mut self, params: u8, layer: ScreenLayer) {
+ fn abs_diff(v0: u16, v1: u16) -> u16 {
+ let v = v1.wrapping_sub(v0);
+ if v > 0x8000 { !v + 1 } else { v }
+ }
+ let [p0, p1] = [self.cursor, self.vector];
+
+ // If the slope of the line is greater than 1.
+ if abs_diff(p0.y, p1.y) > abs_diff(p0.x, p1.x) {
+ // Swap points 0 and 1 such that y0 is always smaller than y1.
+ let (x0, y0, x1, y1) = match p0.y > p1.y {
+ true => (p1.x, p1.y, p0.x, p0.y),
+ false => (p0.x, p0.y, p1.x, p1.y),
+ };
+ let dy = y1 - y0;
+ let (dx, xi) = match x0 > x1 {
+ true => (x0 - x1, 0xffff),
+ false => (x1 - x0, 0x0001),
+ };
+ let dxdy2 = (dx.wrapping_sub(dy)).wrapping_mul(2);
+ let dx2 = dx * 2;
+ let mut d = dx2.wrapping_sub(dy);
+ let mut x = x0;
+
+ for y in y0..=y1 {
+ self.draw_pixel_1bit(params, layer, ScreenPosition::new(x, y));
+ if d < 0x8000 {
+ x = x.wrapping_add(xi); d = d.wrapping_add(dxdy2);
+ } else {
+ d = d.wrapping_add(dx2);
+ }
+ }
+ // If the slope of the line is less than or equal to 1.
+ } else {
+ // Swap points 0 and 1 so that x0 is always smaller than x1.
+ let (x0, y0, x1, y1) = match p0.x > p1.x {
+ true => (p1.x, p1.y, p0.x, p0.y),
+ false => (p0.x, p0.y, p1.x, p1.y),
+ };
+ let dx = x1 - x0;
+ let (dy, yi) = match y0 > y1 {
+ true => (y0 - y1, 0xffff),
+ false => (y1 - y0, 0x0001),
+ };
+ let dydx2 = (dy.wrapping_sub(dx)).wrapping_mul(2);
+ let dy2 = dy * 2;
+ let mut d = dy2.wrapping_sub(dx);
+ let mut y = y0;
+
+ for x in x0..=x1 {
+ self.draw_pixel_1bit(params, layer, ScreenPosition::new(x, y));
+ if d < 0x8000 {
+ y = y.wrapping_add(yi);
+ d = d.wrapping_add(dydx2);
+ } else {
+ d = d.wrapping_add(dy2);
+ }
+ }
+ }
+ }
+
+
+
+}
diff --git a/src/devices/screen/draw_rect.rs b/src/devices/screen/draw_rect.rs
new file mode 100644
index 0000000..265a87f
--- /dev/null
+++ b/src/devices/screen/draw_rect.rs
@@ -0,0 +1,42 @@
+use super::*;
+
+impl ScreenDevice {
+ pub fn draw_rect(&mut self, colour: u8, layer: ScreenLayer) {
+ if let Some([x0, y0, x1, y1]) = self.find_vector_bounding_box() {
+ let screen_width = self.dimensions.width as usize;
+ let rect_width = x1 - x0 + 1;
+ let mut i = x0 + (screen_width * y0);
+ let buffer = match layer {
+ ScreenLayer::Background => &mut self.background,
+ ScreenLayer::Foreground => &mut self.foreground,
+ };
+ for _ in y0..=y1 {
+ buffer[i..i+rect_width].fill(colour);
+ i += screen_width;
+ }
+ }
+ }
+
+ pub fn draw_rect_1bit(&mut self, params: u8, layer: ScreenLayer) {
+ if let Some([x0, y0, x1, y1]) = self.find_vector_bounding_box() {
+ let screen_width = self.dimensions.width as usize;
+ let rect_width = x1 - x0 + 1;
+ let mut i = x0 + (screen_width * y0);
+ let sprite = self.sprite_buffer.get_1bit_sprite(params);
+ let buffer = match layer {
+ ScreenLayer::Background => &mut self.background,
+ ScreenLayer::Foreground => &mut self.foreground,
+ };
+
+ for y in y0..=y1 {
+ let row = sprite[y % 8];
+ for x in x0..=x1 {
+ let colour = row[x % 8];
+ if colour != 0xff { buffer[i] = colour }
+ i += 1;
+ }
+ i += screen_width - rect_width;
+ }
+ };
+ }
+}
diff --git a/src/devices/screen/draw_sprite.rs b/src/devices/screen/draw_sprite.rs
new file mode 100644
index 0000000..9b0658c
--- /dev/null
+++ b/src/devices/screen/draw_sprite.rs
@@ -0,0 +1,25 @@
+use super::*;
+
+impl ScreenDevice {
+ pub fn draw_sprite_1bit(&mut self, params: u8, layer: ScreenLayer) {
+ let sprite = self.sprite_buffer.get_1bit_sprite(params);
+ self.draw_sprite(sprite, layer);
+ }
+
+ pub fn draw_sprite_2bit(&mut self, params: u8, layer: ScreenLayer) {
+ let sprite = self.sprite_buffer.get_2bit_sprite(params);
+ self.draw_sprite(sprite, layer);
+ }
+
+ fn draw_sprite(&mut self, sprite: Sprite, layer: ScreenLayer) {
+ let mut pos = self.cursor;
+ for row in sprite {
+ for colour in row {
+ self.draw_pixel(colour, layer, pos);
+ pos.x = pos.x.wrapping_add(1);
+ }
+ pos.x = pos.x.wrapping_sub(8);
+ pos.y = pos.x.wrapping_add(1);
+ }
+ }
+}
diff --git a/src/devices/screen/sprite_data.rs b/src/devices/screen/sprite_data.rs
index aade2c6..0a1d3c2 100644
--- a/src/devices/screen/sprite_data.rs
+++ b/src/devices/screen/sprite_data.rs
@@ -1,11 +1,17 @@
-pub struct SpriteData {
+use super::*;
+
+macro_rules! test { ($value:expr, $mask:expr) => { $value & $mask != 0 }; }
+
+
+pub struct SpriteBuffer {
data: [u8; 16],
pointer: usize,
+ colours: [u8; 4],
}
-impl SpriteData {
+impl SpriteBuffer {
pub fn new() -> Self {
- Self { data: [0; 16], pointer: 0 }
+ Self { data: [0; 16], pointer: 0, colours: [0; 4] }
}
pub fn push(&mut self, val: u8) {
@@ -13,21 +19,91 @@ impl SpriteData {
self.pointer = (self.pointer + 1) % 16;
}
- pub fn get_1bit_sprite(&self) -> [u8; 8] {
- let mut sprite = [0u8; 8];
- for (i, r) in sprite.iter_mut().enumerate() {
- *r = self.data[(self.pointer + i + 8) % 16]
+ pub fn set_colour_high(&mut self, val: u8) {
+ self.colours[0] = val >> 4;
+ self.colours[1] = val & 0x0f;
+ }
+
+ pub fn set_colour_low(&mut self, val: u8) {
+ self.colours[2] = val >> 4;
+ self.colours[3] = val & 0x0f;
+ }
+
+ // Return the 64 transformed pixels of the current 1-bit sprite.
+ // Each pixel is the palette index of that pixel, or 0xff if transparent.
+ pub fn get_1bit_sprite(&self, params: u8) -> Sprite {
+ let mut sprite = [[0u8; 8]; 8];
+ let plane = self.get_low_plane(params);
+ let colours = self.get_colours(params);
+ for (y, row) in plane.into_iter().enumerate() {
+ for x in (0..8).rev() {
+ sprite[y][7-x] = colours[(row >> x & 0x1) as usize];
+ }
}
return sprite;
}
- pub fn get_2bit_sprite(&self) -> [u8; 16] {
- let mut sprite = [0u8; 16];
- for (i, r) in sprite.iter_mut().enumerate() {
- *r = self.data[(self.pointer + i) % 16]
+ // Return the 64 transformed pixels of the current 2-bit sprite.
+ // Each pixel is the palette index of that pixel, or 0xff if transparent.
+ pub fn get_2bit_sprite(&self, params: u8) -> Sprite {
+ let mut sprite = [[0u8; 8]; 8];
+ let high_plane = self.get_high_plane(params);
+ let low_plane = self.get_low_plane(params);
+ let colours = self.get_colours(params);
+ for (y, (row_h, row_l)) in zip(high_plane, low_plane).enumerate() {
+ for x in (0..8).rev() {
+ let bit_h = (row_h >> x) & 0x1;
+ let bit_l = (row_l >> x) & 0x1;
+ sprite[y][7-x] = colours[(bit_h << 1 | bit_l) as usize];
+ }
}
return sprite;
}
+
+ fn get_high_plane(&self, params: u8) -> Plane {
+ let mut plane = [0u8; 8];
+ for (i, row) in plane.iter_mut().enumerate() {
+ *row = self.data[(self.pointer + i) % 16]
+ }
+ transform_plane(plane, params)
+ }
+
+ fn get_low_plane(&self, params: u8) -> Plane {
+ let mut plane = [0u8; 8];
+ for (i, row) in plane.iter_mut().enumerate() {
+ *row = self.data[(self.pointer + i + 8) % 16]
+ }
+ transform_plane(plane, params)
+ }
+
+ fn get_colours(&self, params: u8) -> [u8; 4] {
+ let mut colours = self.colours;
+ if test!(params, TRANSPARENT) {
+ colours[0] = 0xff;
+ }
+ return colours;
+ }
}
+fn transform_plane(mut plane: Plane, params: u8) -> Plane {
+ if test!(params, FLIP_DIAGONAL) {
+ let mut flipped = [0u8; 8];
+ for mut row in plane {
+ for y in 0..8 {
+ flipped[y] = flipped[y] << 1 | row >> 7;
+ row <<= 1;
+ }
+ }
+ plane = flipped;
+ }
+ if test!(params, FLIP_VERTICAL) {
+ plane.reverse();
+ }
+ if test!(params, FLIP_HORIZONTAL) {
+ for row in plane.iter_mut() {
+ *row = row.reverse_bits();
+ }
+ }
+ return plane;
+}
diff --git a/src/devices/screen/vector_points.rs b/src/devices/screen/vector_points.rs
deleted file mode 100644
index e6c28ef..0000000
--- a/src/devices/screen/vector_points.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use crate::*;
-
-pub struct VectorPoints {
- points: [ScreenPosition; 2],
- pointer: usize,
-}
-
-impl VectorPoints {
- pub fn new() -> Self {
- Self { points: [ScreenPosition::ZERO; 2], pointer: 0 }
- }
-
- pub fn push(&mut self, point: ScreenPosition) {
- self.points[self.pointer] = point;
- self.pointer = (self.pointer + 1) % 2;
- }
-
- pub fn get_pair(&self) -> [ScreenPosition; 2] {
- self.points
- }
-}