summaryrefslogtreecommitdiff
path: root/src/devices/screen_device.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/screen_device.rs')
-rw-r--r--src/devices/screen_device.rs296
1 files changed, 123 insertions, 173 deletions
diff --git a/src/devices/screen_device.rs b/src/devices/screen_device.rs
index a10ab20..483bcca 100644
--- a/src/devices/screen_device.rs
+++ b/src/devices/screen_device.rs
@@ -1,21 +1,18 @@
use crate::*;
-use bedrock_core::*;
use geometry::*;
use phosphor::*;
-type Sprite = [[u8; 8]; 8];
+
+pub type Sprite = [[u8; 8]; 8];
+
#[derive(Clone, Copy)]
pub enum Layer { Fg, Bg }
pub struct ScreenDevice {
- pub wake: bool,
- pub accessed: bool,
-
/// Each byte represents a screen pixel, left-to-right and top-to-bottom.
// Only the bottom four bits of each byte are used.
- // TODO: Consider using the high bit of each pixel byte as a dirty bit.
pub fg: Vec<u8>,
pub bg: Vec<u8>,
pub dirty: bool,
@@ -32,18 +29,110 @@ pub struct ScreenDevice {
pub palette_write: u16,
pub palette: [Colour; 16],
- pub colours: u16,
+ pub sprite_colours: u16,
pub sprite: SpriteBuffer,
+
+ pub accessed: bool,
+ pub wake: bool,
+}
+
+
+impl HasDimensions<u16> for ScreenDevice {
+ fn dimensions(&self) -> ScreenDimensions {
+ self.dimensions
+ }
+}
+
+
+impl Device for ScreenDevice {
+ fn read(&mut self, port: u8) -> u8 {
+ self.accessed = true;
+ match port {
+ 0x0 => read_h!(self.cursor.x),
+ 0x1 => read_l!(self.cursor.x),
+ 0x2 => read_h!(self.cursor.y),
+ 0x3 => read_l!(self.cursor.y),
+ 0x4 => read_h!(self.dimensions.width),
+ 0x5 => read_l!(self.dimensions.width),
+ 0x6 => read_h!(self.dimensions.height),
+ 0x7 => read_l!(self.dimensions.height),
+ 0x8 => 0,
+ 0x9 => 0,
+ 0xA => 0,
+ 0xB => 0,
+ 0xC => 0,
+ 0xD => 0,
+ 0xE => 0,
+ 0xF => 0,
+ _ => unreachable!(),
+ }
+ }
+
+ fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
+ let signal = if self.accessed { None } else { Some(Signal::Break) };
+ self.accessed = true;
+ match port {
+ 0x0 => write_h!(self.cursor.x, value),
+ 0x1 => write_l!(self.cursor.x, value),
+ 0x2 => write_h!(self.cursor.y, value),
+ 0x3 => write_l!(self.cursor.y, value),
+ 0x4 => write_h!(self.width_write, value),
+ 0x5 => { write_l!(self.width_write, value); self.resize_width() }
+ 0x6 => write_h!(self.height_write, value),
+ 0x7 => { write_l!(self.height_write, value); self.resize_height() }
+ 0x8 => write_h!(self.palette_write, value),
+ 0x9 => { write_l!(self.palette_write, value); self.set_palette() }
+ 0xA => write_h!(self.sprite_colours, value),
+ 0xB => write_l!(self.sprite_colours, value),
+ 0xC => self.sprite.push_byte(value),
+ 0xD => self.sprite.push_byte(value),
+ 0xE => self.draw_dispatch(value),
+ 0xF => self.move_cursor(value),
+ _ => unreachable!(),
+ };
+ return signal;
+ }
+
+ fn wake(&mut self) -> bool {
+ self.accessed = true;
+ std::mem::take(&mut self.wake)
+ }
+
+ fn reset(&mut self) {
+ self.fg.clear();
+ self.bg.clear();
+ self.dirty = false;
+
+ self.cursor = ScreenPosition::ZERO;
+ self.vector = ScreenPosition::ZERO;
+
+ self.dirty_dimensions = true;
+ self.width_write = 0;
+ self.height_write = 0;
+ self.fixed_width = None;
+ self.fixed_height = None;
+
+ self.palette_write = 0;
+ self.palette = [
+ Colour::grey(0x00), Colour::grey(0xFF), Colour::grey(0x55), Colour::grey(0xAA),
+ Colour::grey(0x00), Colour::grey(0xFF), Colour::grey(0x55), Colour::grey(0xAA),
+ Colour::grey(0x00), Colour::grey(0xFF), Colour::grey(0x55), Colour::grey(0xAA),
+ Colour::grey(0x00), Colour::grey(0xFF), Colour::grey(0x55), Colour::grey(0xAA),
+ ];
+ self.sprite_colours = 0;
+ self.sprite = SpriteBuffer::new();
+
+ self.accessed = false;
+ self.wake = false;
+ }
}
+
impl ScreenDevice {
pub fn new(config: &EmulatorConfig) -> Self {
let area = config.dimensions.area_usize();
Self {
- wake: false,
- accessed: false,
-
fg: vec![0; area],
bg: vec![0; area],
dirty: false,
@@ -60,12 +149,15 @@ impl ScreenDevice {
palette_write: 0,
palette: [Colour::BLACK; 16],
- colours: 0,
+ sprite_colours: 0,
sprite: SpriteBuffer::new(),
+
+ accessed: false,
+ wake: false,
}
}
- /// External resize.
+ /// Resize screen to match window dimensions.
pub fn resize(&mut self, dimensions: phosphor::Dimensions) {
// Replace dimensions with fixed dimensions.
let screen_dimensions = ScreenDimensions {
@@ -158,9 +250,9 @@ impl ScreenDevice {
pub fn set_palette(&mut self) {
let i = (self.palette_write >> 12 ) as usize;
- let r = (self.palette_write >> 8 & 0xf) as u8 * 17;
- let g = (self.palette_write >> 4 & 0xf) as u8 * 17;
- let b = (self.palette_write & 0xf) as u8 * 17;
+ let r = (self.palette_write >> 8 & 0xF) as u8 * 17;
+ let g = (self.palette_write >> 4 & 0xF) as u8 * 17;
+ let b = (self.palette_write & 0xF) as u8 * 17;
let colour = Colour::from_rgb(r, g, b);
if self.palette[i] != colour {
self.palette[i] = colour;
@@ -193,7 +285,7 @@ impl ScreenDevice {
}
pub fn move_cursor(&mut self, value: u8) {
- let distance = (value & 0x3f) as u16;
+ let distance = (value & 0x3F) as u16;
match value >> 6 {
0b00 => self.cursor.x = self.cursor.x.wrapping_add(distance),
0b01 => self.cursor.y = self.cursor.y.wrapping_add(distance),
@@ -203,7 +295,7 @@ impl ScreenDevice {
};
}
- /// Colour must already be masked by 0xf.
+ /// Colour must already be masked by 0xF.
pub fn draw_pixel(&mut self, layer: Layer, x: u16, y: u16, colour: u8) {
if x < self.dimensions.width && y < self.dimensions.height {
let index = x as usize + (self.dimensions.width as usize * y as usize);
@@ -215,13 +307,13 @@ impl ScreenDevice {
}
fn op_draw_pixel(&mut self, layer: Layer, draw: u8) {
- self.draw_pixel(layer, self.cursor.x, self.cursor.y, draw & 0xf);
+ self.draw_pixel(layer, self.cursor.x, self.cursor.y, draw & 0xF);
}
fn op_fill_layer(&mut self, layer: Layer, draw: u8) {
match layer {
- Layer::Fg => self.fg.fill(draw & 0xf),
- Layer::Bg => self.bg.fill(draw & 0xf),
+ Layer::Fg => self.fg.fill(draw & 0xF),
+ Layer::Bg => self.bg.fill(draw & 0xF),
}
}
@@ -231,10 +323,10 @@ impl ScreenDevice {
false => self.sprite.read_1bit_sprite(draw),
};
let colours = [
- (self.colours >> 12 & 0x000f) as u8,
- (self.colours >> 8 & 0x000f) as u8,
- (self.colours >> 4 & 0x000f) as u8,
- (self.colours & 0x000f) as u8,
+ (self.sprite_colours >> 12 & 0x000F) as u8,
+ (self.sprite_colours >> 8 & 0x000F) as u8,
+ (self.sprite_colours >> 4 & 0x000F) as u8,
+ (self.sprite_colours & 0x000F) as u8,
];
let cx = self.cursor.x;
let cy = self.cursor.y;
@@ -279,8 +371,8 @@ impl ScreenDevice {
if draw & 0x10 != 0 {
// Draw 1-bit textured line.
let sprite = self.sprite.read_1bit_sprite(draw);
- let c1 = (self.colours >> 8 & 0xf) as u8;
- let c0 = (self.colours >> 12 & 0xf) as u8;
+ let c1 = (self.sprite_colours >> 8 & 0xF) as u8;
+ let c0 = (self.sprite_colours >> 12 & 0xF) as u8;
let opaque = draw & 0x08 == 0;
loop {
let sprite_pixel = sprite[(y as usize) % 8][(x as usize) % 8];
@@ -293,7 +385,7 @@ impl ScreenDevice {
}
} else {
// Draw solid line.
- let colour = draw & 0xf;
+ let colour = draw & 0xF;
loop {
self.draw_pixel(layer, x as u16, y as u16, colour);
if x == x_end && y == y_end { break; }
@@ -307,7 +399,7 @@ impl ScreenDevice {
fn op_draw_rect(&mut self, layer: Layer, draw: u8) {
macro_rules! clamp {
($v:expr, $max:expr) => {
- if $v > 0x7fff { 0 } else if $v > $max { $max } else { $v }
+ if $v > 0x7FFF { 0 } else if $v > $max { $max } else { $v }
};
}
macro_rules! out_of_bounds {
@@ -333,8 +425,8 @@ impl ScreenDevice {
if draw & 0x10 != 0 {
// Draw 1-bit textured rectangle.
let sprite = self.sprite.read_1bit_sprite(draw);
- let c1 = (self.colours >> 8 & 0xf) as u8;
- let c0 = (self.colours >> 12 & 0xf) as u8;
+ let c1 = (self.sprite_colours >> 8 & 0xF) as u8;
+ let c0 = (self.sprite_colours >> 12 & 0xF) as u8;
let opaque = draw & 0x08 == 0;
for y in t..=b {
for x in l..=r {
@@ -345,7 +437,7 @@ impl ScreenDevice {
}
} else {
// Draw solid rectangle.
- let colour = draw & 0xf;
+ let colour = draw & 0xF;
for y in t..=b {
for x in l..=r {
self.draw_pixel(layer, x, y, colour);
@@ -355,146 +447,4 @@ impl ScreenDevice {
}
}
-impl Device for ScreenDevice {
- fn read(&mut self, port: u8) -> u8 {
- self.accessed = true;
- match port {
- 0x0 => read_h!(self.dimensions.width),
- 0x1 => read_l!(self.dimensions.width),
- 0x2 => read_h!(self.dimensions.height),
- 0x3 => read_l!(self.dimensions.height),
- 0x4 => read_h!(self.cursor.x),
- 0x5 => read_l!(self.cursor.x),
- 0x6 => read_h!(self.cursor.y),
- 0x7 => read_l!(self.cursor.y),
- 0x8 => 0,
- 0x9 => 0,
- 0xa => 0,
- 0xb => 0,
- 0xc => 0,
- 0xd => 0,
- 0xe => 0,
- 0xf => 0,
- _ => unreachable!(),
- }
- }
- fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
- self.accessed = true;
- match port {
- 0x0 => write_h!(self.width_write, value),
- 0x1 => { write_l!(self.width_write, value); self.resize_width(); },
- 0x2 => write_h!(self.height_write, value),
- 0x3 => { write_l!(self.height_write, value); self.resize_height(); },
- 0x4 => write_h!(self.cursor.x, value),
- 0x5 => write_l!(self.cursor.x, value),
- 0x6 => write_h!(self.cursor.y, value),
- 0x7 => write_l!(self.cursor.y, value),
- 0x8 => write_h!(self.palette_write, value),
- 0x9 => { write_l!(self.palette_write, value); self.set_palette(); },
- 0xa => write_h!(self.colours, value),
- 0xb => write_l!(self.colours, value),
- 0xc => self.sprite.push_byte(value),
- 0xd => self.sprite.push_byte(value),
- 0xe => self.draw_dispatch(value),
- 0xf => self.move_cursor(value),
- _ => unreachable!(),
- };
- return None;
- }
-
- fn wake(&mut self) -> bool {
- self.accessed = true;
- std::mem::take(&mut self.wake)
- }
-}
-
-impl HasDimensions<u16> for ScreenDevice {
- fn dimensions(&self) -> ScreenDimensions {
- self.dimensions
- }
-}
-
-
-pub struct SpriteBuffer {
- pub mem: [u8; 16],
- pub pointer: usize,
- pub cached: Option<(Sprite, u8)>,
-}
-
-impl SpriteBuffer {
- pub fn new() -> Self {
- Self {
- mem: [0; 16],
- pointer: 0,
- cached: None,
- }
- }
-
- pub fn push_byte(&mut self, byte: u8) {
- self.mem[self.pointer] = byte;
- self.pointer = (self.pointer + 1) % 16;
- self.cached = None;
- }
-
- pub fn read_1bit_sprite(&mut self, draw: u8) -> Sprite {
- if let Some((sprite, transform)) = self.cached {
- if transform == (draw & 0x77) {
- return sprite;
- }
- }
- macro_rules! c {
- ($v:ident=mem[$p:ident++]) => { let $v = self.mem[$p % 16]; $p = $p.wrapping_add(1); };
- ($v:ident=mem[--$p:ident]) => { $p = $p.wrapping_sub(1); let $v = self.mem[$p % 16]; };
- }
- let mut sprite = [[0; 8]; 8];
- let mut p = match draw & 0x02 != 0 {
- true => self.pointer,
- false => self.pointer + 8,
- };
- match draw & 0x07 {
- 0x0 => { for y in 0..8 { c!(l=mem[p++]); for x in 0..8 { sprite[y][x] = l>>(7-x) & 1; } } },
- 0x1 => { for y in 0..8 { c!(l=mem[p++]); for x in 0..8 { sprite[y][x] = l>>( x) & 1; } } },
- 0x2 => { for y in 0..8 { c!(l=mem[--p]); for x in 0..8 { sprite[y][x] = l>>(7-x) & 1; } } },
- 0x3 => { for y in 0..8 { c!(l=mem[--p]); for x in 0..8 { sprite[y][x] = l>>( x) & 1; } } },
- 0x4 => { for y in 0..8 { c!(l=mem[p++]); for x in 0..8 { sprite[x][y] = l>>(7-x) & 1; } } },
- 0x5 => { for y in 0..8 { c!(l=mem[p++]); for x in 0..8 { sprite[x][y] = l>>( x) & 1; } } },
- 0x6 => { for y in 0..8 { c!(l=mem[--p]); for x in 0..8 { sprite[x][y] = l>>(7-x) & 1; } } },
- 0x7 => { for y in 0..8 { c!(l=mem[--p]); for x in 0..8 { sprite[x][y] = l>>( x) & 1; } } },
- _ => unreachable!(),
- }
- self.cached = Some((sprite, draw & 0x77));
- return sprite;
- }
-
- pub fn read_2bit_sprite(&mut self, draw: u8) -> Sprite {
- if let Some((sprite, transform)) = self.cached {
- if transform == (draw & 0x77) {
- return sprite;
- }
- }
- macro_rules! c {
- ($v:ident=mem[$p:ident++]) => { let $v = self.mem[$p % 16]; $p = $p.wrapping_add(1); };
- ($v:ident=mem[--$p:ident]) => { $p = $p.wrapping_sub(1); let $v = self.mem[$p % 16]; };
- }
- let mut sprite = [[0; 8]; 8];
- let mut p = match draw & 0x02 != 0 {
- true => self.pointer,
- false => self.pointer + 8,
- };
- let mut s = p + 8;
- match draw & 0x07 {
- 0x0 => for y in 0..8 { c!(l=mem[p++]); c!(h=mem[s++]); for x in 0..8 { let i=7-x; sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x1 => for y in 0..8 { c!(l=mem[p++]); c!(h=mem[s++]); for x in 0..8 { let i= x; sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x2 => for y in 0..8 { c!(l=mem[--p]); c!(h=mem[--s]); for x in 0..8 { let i=7-x; sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x3 => for y in 0..8 { c!(l=mem[--p]); c!(h=mem[--s]); for x in 0..8 { let i= x; sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x4 => for y in 0..8 { c!(l=mem[p++]); c!(h=mem[s++]); for x in 0..8 { let i=7-x; sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x5 => for y in 0..8 { c!(l=mem[p++]); c!(h=mem[s++]); for x in 0..8 { let i= x; sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x6 => for y in 0..8 { c!(l=mem[--p]); c!(h=mem[--s]); for x in 0..8 { let i=7-x; sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } },
- 0x7 => for y in 0..8 { c!(l=mem[--p]); c!(h=mem[--s]); for x in 0..8 { let i= x; sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } },
- _ => unreachable!(),
- }
- self.cached = Some((sprite, draw & 0x77));
- return sprite;
- }
-}