diff options
Diffstat (limited to 'src/devices/screen_device.rs')
-rw-r--r-- | src/devices/screen_device.rs | 296 |
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; - } -} |