summaryrefslogtreecommitdiff
path: root/src/devices/screen.rs
diff options
context:
space:
mode:
authorBen Bridle <bridle.benjamin@gmail.com>2024-10-28 20:25:01 +1300
committerBen Bridle <bridle.benjamin@gmail.com>2024-10-28 20:29:12 +1300
commit1a830a3d1b9d99653322d5ae49ea8165de7ed9d0 (patch)
tree798e77b6fcf2438b1c2538a67efe856a2f7cb979 /src/devices/screen.rs
parent03c4b069e1806af256730639cefdae115b24401a (diff)
downloadbedrock-pc-1a830a3d1b9d99653322d5ae49ea8165de7ed9d0.zip
Rewrite emulatorv1.0.0-alpha1
This is a complete rewrite and restructure of the entire emulator project, as part of the effort in locking down the Bedrock specification and in creating much better tooling for creating and using Bedrock programs. This commit adds a command-line argument scheme, an embedded assembler, a headless emulator for use in non-graphical environments, deferred window creation for programs that do not access the screen device, and new versions of phosphor and bedrock-core. The new version of phosphor supports multi-window programs, which will make it possible to implement program forking in the system device later on, and the new version of bedrock-core implements the final core specification.
Diffstat (limited to 'src/devices/screen.rs')
-rw-r--r--src/devices/screen.rs258
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,
- ])
- }
- }
-}