diff options
| author | Ben Bridle <bridle.benjamin@gmail.com> | 2024-04-16 10:51:13 +1200 | 
|---|---|---|
| committer | Ben Bridle <bridle.benjamin@gmail.com> | 2024-04-16 10:51:26 +1200 | 
| commit | 6b3796c9a0d3a2f1422bcbde4790c43417659722 (patch) | |
| tree | 6429a5fa2f8c4d3b26790775e07e46e6338b61d3 /src/devices | |
| parent | 28101de56231252ca0cfa6a9f107b75112c9acad (diff) | |
| download | bedrock-pc-6b3796c9a0d3a2f1422bcbde4790c43417659722.zip | |
Update devices to match new specifications
Diffstat (limited to 'src/devices')
| -rw-r--r-- | src/devices/clock.rs | 57 | ||||
| -rw-r--r-- | src/devices/file/directory_entry.rs | 2 | ||||
| -rw-r--r-- | src/devices/file/directory_listing.rs | 18 | ||||
| -rw-r--r-- | src/devices/file/entry.rs | 2 | ||||
| -rw-r--r-- | src/devices/input.rs | 176 | ||||
| -rw-r--r-- | src/devices/math.rs | 28 | ||||
| -rw-r--r-- | src/devices/memory.rs | 89 | ||||
| -rw-r--r-- | src/devices/scratch.rs | 61 | ||||
| -rw-r--r-- | src/devices/screen.rs | 344 | ||||
| -rw-r--r-- | src/devices/screen/draw_line.rs | 223 | ||||
| -rw-r--r-- | src/devices/screen/draw_rect.rs | 42 | ||||
| -rw-r--r-- | src/devices/screen/draw_sprite.rs | 25 | ||||
| -rw-r--r-- | src/devices/screen/sprite_data.rs | 98 | ||||
| -rw-r--r-- | src/devices/screen/vector_points.rs | 21 | ||||
| -rw-r--r-- | src/devices/system.rs | 4 | ||||
| -rw-r--r-- | src/devices/system/read_only_text_buffer.rs | 23 | 
16 files changed, 721 insertions, 492 deletions
| diff --git a/src/devices/clock.rs b/src/devices/clock.rs index 3faa604..60b1cad 100644 --- a/src/devices/clock.rs +++ b/src/devices/clock.rs @@ -1,22 +1,16 @@  use std::time::{Duration, Instant}; +macro_rules! to_ticks { ($dur:expr) => {($dur.as_millis() / 4) as u16}; } +macro_rules! from_ticks { ($ticks:expr) => {Duration::from_millis(($ticks * 4).into())}; }  macro_rules! now { () => { -    time::OffsetDateTime::now_local() -    .unwrap_or_else(|_| time::OffsetDateTime::now_utc()) -};} - -macro_rules! to_ticks { -    ($duration:expr) => {($duration.as_millis() / 4) as u16}; } -macro_rules! from_ticks { -    ($ticks:expr) => {Duration::from_millis(($ticks * 4).into())}; } - +    time::OffsetDateTime::now_local().unwrap_or_else(|_| time::OffsetDateTime::now_utc())};}  /// Create a method to set the instant of a timer.  macro_rules! generate_set_timer_method {      ($i:tt) => { mini_paste::item!{          pub fn [< set_timer_ $i >] (&mut self) {              let ticks = &mut self. [< timer_ $i >]; -            let instant = &mut self. [< timer_ $i _instant >]; +            let instant = &mut self. [< timer_ $i _end >];              *instant = Instant::now() + from_ticks!(*ticks);          } @@ -28,13 +22,13 @@ macro_rules! generate_update_timer_method {      ($i:tt) => { mini_paste::item!{          pub fn [< update_timer_ $i >] (&mut self) -> u16 {              let ticks = &mut self. [< timer_ $i >]; -            let instant = &mut self. [< timer_ $i _instant >]; +            let instant = &mut self. [< timer_ $i _end >];              if *ticks > 0 {                  *ticks = to_ticks!(instant.duration_since(Instant::now()));                  if *ticks == 0 { self.wake_flag = true; }              } -            *ticks +            return *ticks;          }      }};  } @@ -42,13 +36,13 @@ macro_rules! generate_update_timer_method {  pub struct ClockDevice {      pub wake_flag: bool, -    pub boot_time: Instant, -    pub cumulative_timer: u16, +    pub program_start: Instant, +    pub uptime: u16, -    pub timer_1_instant: Instant, -    pub timer_2_instant: Instant, -    pub timer_3_instant: Instant, -    pub timer_4_instant: Instant, +    pub timer_1_end: Instant, +    pub timer_2_end: Instant, +    pub timer_3_end: Instant, +    pub timer_4_end: Instant,      pub timer_1: u16,      pub timer_2: u16,      pub timer_3: u16, @@ -60,13 +54,13 @@ impl ClockDevice {          Self {              wake_flag: false, -            boot_time: Instant::now(), -            cumulative_timer: 0, +            program_start: Instant::now(), +            uptime: 0, -            timer_1_instant: Instant::now(), -            timer_2_instant: Instant::now(), -            timer_3_instant: Instant::now(), -            timer_4_instant: Instant::now(), +            timer_1_end: Instant::now(), +            timer_2_end: Instant::now(), +            timer_3_end: Instant::now(), +            timer_4_end: Instant::now(),              timer_1: 0,              timer_2: 0,              timer_3: 0, @@ -84,9 +78,16 @@ impl ClockDevice {      generate_update_timer_method!{3}      generate_update_timer_method!{4} -    pub fn update_cumulative_timer(&mut self) -> u16 { -        self.cumulative_timer = to_ticks!(self.boot_time.elapsed()); -        return self.cumulative_timer; +    pub fn update_timers(&mut self) { +        self.update_timer_1(); +        self.update_timer_2(); +        self.update_timer_3(); +        self.update_timer_4(); +    } + +    pub fn update_uptime(&mut self) -> u16 { +        self.uptime = to_ticks!(self.program_start.elapsed()); +        return self.uptime;      }      pub fn year(&self) -> u8 { @@ -113,7 +114,7 @@ impl ClockDevice {          now!().second()      } -    pub fn shortest_active_timer(&self) -> Option<Duration> { +    pub fn time_to_next_wake(&self) -> Option<Duration> {          [self.timer_1, self.timer_2, self.timer_3, self.timer_4]              .iter()              .filter(|t| **t > 0) diff --git a/src/devices/file/directory_entry.rs b/src/devices/file/directory_entry.rs index 45de817..c4ce146 100644 --- a/src/devices/file/directory_entry.rs +++ b/src/devices/file/directory_entry.rs @@ -1,4 +1,4 @@ -use crate::*; +use super::*;  use std::cmp::Ordering; diff --git a/src/devices/file/directory_listing.rs b/src/devices/file/directory_listing.rs index 1f94a3a..1295f8b 100644 --- a/src/devices/file/directory_listing.rs +++ b/src/devices/file/directory_listing.rs @@ -1,4 +1,4 @@ -use crate::*; +use super::*;  use std::ffi::OsString;  use std::os::unix::ffi::{OsStrExt, OsStringExt}; @@ -8,7 +8,7 @@ use std::path::{Component, Path, PathBuf};  pub struct DirectoryListing {      children: Vec<DirectoryChild>,      length: u32, -    selected: u32, +    selected: Option<u32>,      name_buffer: CircularPathBuffer,  } @@ -44,7 +44,7 @@ impl DirectoryListing {              }              children.sort_unstable();              let length = u32::try_from(children.len()).unwrap_or(u32::MAX); -            let selected = 0; +            let selected = None;              let name_buffer = CircularPathBuffer::new();              Ok(Self { children, length, selected, name_buffer } )          } else { @@ -61,16 +61,16 @@ impl DirectoryListing {      }      pub fn selected(&self) -> u32 { -        self.selected +        self.selected.unwrap_or(0)      }      pub fn set_selected(&mut self, index: u32) {          if let Some(info) = self.get(index) {              self.name_buffer.populate(&info.byte_path.clone()); -            self.selected = index; +            self.selected = Some(index);          } else {              self.name_buffer.clear(); -            self.selected = 0; +            self.selected = None;          }      } @@ -79,14 +79,14 @@ impl DirectoryListing {      }      pub fn child_type(&self) -> Option<EntryType> { -        self.get(self.selected).and_then(|i| Some(i.entry_type)) +        self.selected.and_then(|s| self.get(s).and_then(|i| Some(i.entry_type)))      }      pub fn child_path(&self) -> Option<PathBuf> { -        self.get(self.selected).and_then(|i| { +        self.selected.and_then(|s| self.get(s).and_then(|i| {              let os_string: OsString = OsStringExt::from_vec(i.byte_path.clone());              Some(os_string.into()) -        }) +        }))      }  } diff --git a/src/devices/file/entry.rs b/src/devices/file/entry.rs index a91ae82..d604bb7 100644 --- a/src/devices/file/entry.rs +++ b/src/devices/file/entry.rs @@ -1,4 +1,4 @@ -use crate::*; +use super::*;  use std::cmp::Ordering; diff --git a/src/devices/input.rs b/src/devices/input.rs index f3191dd..f23d902 100644 --- a/src/devices/input.rs +++ b/src/devices/input.rs @@ -1,21 +1,45 @@  use crate::*; - +use phosphor::*;  use std::collections::VecDeque; +const CONTROL:  u8 = 0x80; +const ALT:      u8 = 0x40; +const SHIFT:    u8 = 0x20; + +const UP:       u8 = 0x80; +const DOWN:     u8 = 0x40; +const LEFT:     u8 = 0x20; +const RIGHT:    u8 = 0x10; +const CONFIRM:  u8 = 0x08; +const CANCEL:   u8 = 0x04; +const NEXT:     u8 = 0x02; +const PREVIOUS: u8 = 0x01; +const AUX1:     u8 = 0x02; +const AUX2:     u8 = 0x01; + +macro_rules! test { ($value:expr, $mask:expr) => { $value & $mask != 0 }; } + +  pub struct InputDevice {      pub wake_flag: bool, -    pub mouse_position: ScreenPosition, -    pub mouse_button_state: u8, +    pub pointer_position: ScreenPosition, +    pub pointer_buttons: u8, +    pub pointer_active: bool, + +    pub horizontal_scroll: i8, +    pub vertical_scroll: i8, +    pub horizontal_scroll_delta: f64, +    pub vertical_scroll_delta: f64, -    pub horizontal_scroll_value: u16, -    pub vertical_scroll_value: u16, -    pub horizontal_scroll_value_delta: f64, -    pub vertical_scroll_value_delta: f64, +    pub text_queue: VecDeque<u8>, +    pub modifiers: u8, +    pub navigation: u8, -    pub character_queue: VecDeque<u8>, -    pub modifier_state: u8, -    pub navigation_state: u8, +    pub controller_1: u8, +    pub controller_2: u8, +    pub controller_3: u8, +    pub controller_4: u8,  }  impl InputDevice { @@ -23,104 +47,122 @@ impl InputDevice {          Self {              wake_flag: false, -            mouse_position: ScreenPosition::ZERO, -            mouse_button_state: 0x00, +            pointer_position: ScreenPosition::ZERO, +            pointer_buttons: 0x00, +            pointer_active: false, -            horizontal_scroll_value: 0x0000, -            vertical_scroll_value: 0x0000, -            horizontal_scroll_value_delta: 0.0, -            vertical_scroll_value_delta: 0.0, +            horizontal_scroll: 0x0000, +            vertical_scroll: 0x0000, +            horizontal_scroll_delta: 0.0, +            vertical_scroll_delta: 0.0, -            character_queue: VecDeque::new(), -            modifier_state: 0x00, -            navigation_state: 0x00, +            text_queue: VecDeque::new(), +            modifiers: 0x00, +            navigation: 0x00, + +            controller_1: 0x00, +            controller_2: 0x00, +            controller_3: 0x00, +            controller_4: 0x00,          }      } -    pub fn mouse_button_action(&mut self, mask: u8, action: phosphor::Action) { -        let new_button_state = match action { -            phosphor::Action::Pressed => self.mouse_button_state | mask, -            phosphor::Action::Released => self.mouse_button_state & !mask, +    pub fn read_horizontal_scroll(&mut self) -> u8 { +        let output_value = self.horizontal_scroll; +        self.horizontal_scroll = 0; +        return output_value as u8; +    } + +    pub fn read_vertical_scroll(&mut self) -> u8 { +        let output_value = self.vertical_scroll; +        self.vertical_scroll = 0; +        return output_value as u8; +    } + +    pub fn on_pointer_button(&mut self, mask: u8, action: Action) { +        let new_buttons = match action { +            Action::Pressed => self.pointer_buttons | mask, +            Action::Released => self.pointer_buttons & !mask,          }; -        if new_button_state != self.mouse_button_state { -            self.mouse_button_state = new_button_state; +        if new_buttons != self.pointer_buttons { +            self.pointer_buttons = new_buttons;              self.wake_flag = true;          }      } -    pub fn move_mouse(&mut self, new_mouse_position: ScreenPosition) { -        let old_mouse_position = self.mouse_position; -        if new_mouse_position != old_mouse_position { -            self.mouse_position = new_mouse_position; +    pub fn on_pointer_move(&mut self, position: ScreenPosition) { +        if position != self.pointer_position { +            self.pointer_position = position;              self.wake_flag = true;          }      }      pub fn on_scroll_horizontal(&mut self, delta: f64) { -        self.horizontal_scroll_value_delta += delta; -        while self.horizontal_scroll_value_delta > 20.0 { -            self.horizontal_scroll_value += 1; -            self.horizontal_scroll_value_delta -= 20.0; +        self.horizontal_scroll_delta += delta; +        while self.horizontal_scroll_delta > 1.0 { +            self.horizontal_scroll = self.horizontal_scroll.saturating_add(1); +            self.horizontal_scroll_delta -= 1.0;              self.wake_flag = true;          } -        while self.horizontal_scroll_value_delta < -20.0 { -            self.horizontal_scroll_value -= 1; -            self.horizontal_scroll_value_delta += 20.0; +        while self.horizontal_scroll_delta < -1.0 { +            self.horizontal_scroll = self.horizontal_scroll.saturating_sub(1); +            self.horizontal_scroll_delta += 1.0;              self.wake_flag = true;          }      }      pub fn on_scroll_vertical(&mut self, delta: f64) { -        self.vertical_scroll_value_delta += delta; -        while self.vertical_scroll_value_delta > 20.0 { -            self.vertical_scroll_value += 1; -            self.vertical_scroll_value_delta -= 20.0; +        self.vertical_scroll_delta += delta; +        while self.vertical_scroll_delta > 1.0 { +            self.vertical_scroll = self.vertical_scroll.saturating_add(1); +            self.vertical_scroll_delta -= 1.0;              self.wake_flag = true;          } -        while self.vertical_scroll_value_delta < -20.0 { -            self.vertical_scroll_value -= 1; -            self.vertical_scroll_value_delta += 20.0; +        while self.vertical_scroll_delta < -1.0 { +            self.vertical_scroll = self.vertical_scroll.saturating_sub(1); +            self.vertical_scroll_delta += 1.0;              self.wake_flag = true;          }      }      pub fn on_character_input(&mut self, input: char) { -        if let Ok(ascii) = u8::try_from(u32::from(input)) { -            self.character_queue.push_back(ascii); +        if let Ok(byte) = u8::try_from(u32::from(input)) { +            self.text_queue.push_back(byte);              self.wake_flag = true;          }      } -    pub fn on_keyboard_input(&mut self, input: phosphor::KeyboardInput) { -        let tab = self.modifier_state & 0x40 != 0; +    pub fn on_keyboard_input(&mut self, input: KeyboardInput) {          let mask = match input.key { -            phosphor::KeyCode::Up => 0x80, -            phosphor::KeyCode::Down => 0x40, -            phosphor::KeyCode::Left => 0x20, -            phosphor::KeyCode::Right => 0x10, -            phosphor::KeyCode::Tab => match tab { false => 0x08, true  => 0x04 }, -            phosphor::KeyCode::Return => 0x02, -            phosphor::KeyCode::Escape => 0x01, +            KeyCode::Up     => UP, +            KeyCode::Down   => DOWN, +            KeyCode::Left   => LEFT, +            KeyCode::Right  => RIGHT, +            KeyCode::Return => CONFIRM, +            KeyCode::Escape => CANCEL, +            KeyCode::Tab => match test!(self.modifiers, SHIFT) { +                false => NEXT, +                true  => PREVIOUS +            },              _ => return,          }; -        let new_navigation_state = match input.action { -            phosphor::Action::Pressed => self.navigation_state | mask, -            phosphor::Action::Released =>  self.navigation_state & !mask, +        let navigation = match input.action { +            Action::Pressed => self.navigation | mask, +            Action::Released =>  self.navigation & !mask,          }; -        if new_navigation_state != self.navigation_state { -            self.navigation_state = new_navigation_state; +        if navigation != self.navigation { +            self.navigation = navigation;              self.wake_flag = true;          }      } -    pub fn on_modifier_change(&mut self, modifiers: phosphor::ModifiersState) { -        let mut new_modifier_state = 0x00; -        if modifiers.ctrl()  { new_modifier_state |= 0x80 } -        if modifiers.shift() { new_modifier_state |= 0x40 } -        if modifiers.alt()   { new_modifier_state |= 0x20 } -        if modifiers.logo()  { new_modifier_state |= 0x10 } -        if new_modifier_state != self.modifier_state { -            self.modifier_state = new_modifier_state; +    pub fn on_modifier_change(&mut self, modifiers: ModifiersState) { +        let mut new_modifiers = 0x00; +        if modifiers.ctrl()  { new_modifiers |= CONTROL } +        if modifiers.alt()   { new_modifiers |= ALT     } +        if modifiers.shift() { new_modifiers |= SHIFT   } +        if new_modifiers != self.modifiers { +            self.modifiers = new_modifiers;              self.wake_flag = true;          } diff --git a/src/devices/math.rs b/src/devices/math.rs index c8d2194..cefb572 100644 --- a/src/devices/math.rs +++ b/src/devices/math.rs @@ -1,10 +1,6 @@  pub struct MathDevice {      pub operand_1: u16,      pub operand_2: u16, -    pub product_high: u16, -    pub product_low: u16, -    pub quotient: u16, -    pub remainder: u16,  }  impl MathDevice { @@ -12,24 +8,24 @@ impl MathDevice {          Self {              operand_1: 0x0000,              operand_2: 0x0000, -            product_high: 0x0000, -            product_low: 0x0000, -            quotient: 0x0000, -            remainder: 0x0000,          }      } -    pub fn multiply(&mut self) { -        let (low, high) = self.operand_1.widening_mul(self.operand_2); -        self.product_high = high; -        self.product_low = low; +    pub fn multiply_high(&mut self) -> u16 { +        let (_, high) = self.operand_1.widening_mul(self.operand_2); +        return high;      } -    pub fn divide(&mut self) { -        self.quotient = self.operand_1.checked_div(self.operand_2).unwrap_or(0); +    pub fn multiply_low(&mut self) -> u16 { +        let (low, _) = self.operand_1.widening_mul(self.operand_2); +        return low;      } -    pub fn modulo(&mut self) { -        self.remainder = self.operand_1.checked_rem(self.operand_2).unwrap_or(0); +    pub fn divide(&mut self) -> u16 { +        self.operand_1.checked_div(self.operand_2).unwrap_or(0) +    } + +    pub fn modulo(&mut self) -> u16 { +        self.operand_1.checked_rem(self.operand_2).unwrap_or(0)      }  } diff --git a/src/devices/memory.rs b/src/devices/memory.rs new file mode 100644 index 0000000..1d33f64 --- /dev/null +++ b/src/devices/memory.rs @@ -0,0 +1,89 @@ +macro_rules! then_inc { ($v:expr) => {{ $v = $v.wrapping_add(1); $v as usize }}; } + +type Page = [u8; 65536]; +fn new_blank_page() -> [u8; 65536] { [0; 65536] } + + +pub struct MemoryDevice { +    pages: Vec<Page>, +    pub page_limit: u16, +    pub page_1: u16, +    pub address_1: u16, +    pub page_2: u16, +    pub address_2: u16, +    pub copy_length: u16, +} + +impl MemoryDevice { +    pub fn new() -> Self { +        Self { +            pages: Vec::new(), +            page_limit: 0, +            page_1: 0, +            address_1: 0, +            page_2: 0, +            address_2: 0, +            copy_length: 0, +        } +    } + +    pub fn page_count(&self) -> u16 { +        self.pages.len() as u16 +    } + +    pub fn copy(&mut self) { +        // Return if at least one page is out-of-bounds +        if self.page_1 as usize >= self.pages.len() +        || self.page_2 as usize >= self.pages.len() { +            return +        } +        let p1 = self.page_1 as usize; +        let p2 = self.page_2 as usize; +        let a1 = self.address_1; +        let a2 = self.address_2; + +        for i in 0..=self.copy_length { +            let byte = self.pages[p2][a2.wrapping_add(i) as usize]; +            self.pages[p1][a1.wrapping_add(i) as usize] = byte; +        } +    } + +    pub fn expand_memory(&mut self) { +        let size = std::cmp::max(self.page_1, self.page_2) as usize + 1; +        if size < self.page_limit as usize { +            self.pages.resize_with(size, new_blank_page); +        } +    } + +    pub fn read_from_head_1(&mut self) -> u8 { +        let address = then_inc!(self.address_1); +        if let Some(page) = self.pages.get(self.page_1 as usize) { +            page[address] +        } else { +            0x00 +        } +    } + +    pub fn read_from_head_2(&mut self) -> u8 { +        let address = then_inc!(self.address_2); +        if let Some(page) = self.pages.get(self.page_2 as usize) { +            page[address] +        } else { +            0x00 +        } +    } + +    pub fn write_to_head_1(&mut self, byte: u8) { +        let address = then_inc!(self.address_1); +        if let Some(page) = self.pages.get_mut(self.page_1 as usize) { +            page[address] = byte; +        } +    } + +    pub fn write_to_head_2(&mut self, byte: u8) { +        let address = then_inc!(self.address_2); +        if let Some(page) = self.pages.get_mut(self.page_2 as usize) { +            page[address] = byte; +        } +    } +} diff --git a/src/devices/scratch.rs b/src/devices/scratch.rs deleted file mode 100644 index 950f87a..0000000 --- a/src/devices/scratch.rs +++ /dev/null @@ -1,61 +0,0 @@ -macro_rules! then_inc { -    ($v:expr) => {{ -        $v = $v.wrapping_add(1); -        $v as usize -    }}; -} - -pub struct ScratchDevice { -    memory: Vec<u8>, -    pub max_capacity: u32, -    pub pointer_1: u32, -    pub pointer_2: u32, -} - -impl ScratchDevice { -    pub fn new() -> Self { -        Self { -            memory: Vec::new(), -            max_capacity: 16 * 65536, - -            pointer_1: 0, -            pointer_2: 0, -        } -    } - -    pub fn read_head_1(&mut self) -> u8 { -        let i = then_inc!(self.pointer_1); -        self.read_byte(i) -    } - -    pub fn read_head_2(&mut self) -> u8 { -        let i = then_inc!(self.pointer_2); -        self.read_byte(i) -    } - -    pub fn write_head_1(&mut self, value: u8) { -        let i = then_inc!(self.pointer_1); -        self.write_byte(i, value); -    } - -    pub fn write_head_2(&mut self, value: u8) { -        let i = then_inc!(self.pointer_2); -        self.write_byte(i, value); -    } - -    fn read_byte(&self, pointer: usize) -> u8 { -        match self.memory.get(pointer as usize) { -            Some(value) => *value, -            None => 0, -        } -    } - -    fn write_byte(&mut self, pointer: usize, value: u8) { -        if pointer < self.max_capacity as usize { -            if pointer >= self.memory.len() { -                self.memory.resize(pointer + 1, 0); -            } -            self.memory[pointer] = value; -        } -    } -} diff --git a/src/devices/screen.rs b/src/devices/screen.rs index c96d1c4..4394b6c 100644 --- a/src/devices/screen.rs +++ b/src/devices/screen.rs @@ -1,37 +1,50 @@  mod sprite_data; -mod vector_points; +mod draw_line; +mod draw_rect; +mod draw_sprite;  pub use sprite_data::*; -pub use vector_points::*; -  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-right-top-bottom. + +    /// 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 is_dirty: bool, -    pub is_resizable: bool, +    pub dirty: bool, +    pub resizable: bool,      pub cursor: ScreenPosition, +    pub vector: ScreenPosition,      pub dimensions: ScreenDimensions, -    pub sprite_data: SpriteData, -    pub palette: [Colour; 16], +      pub palette_high: u8, -    pub sprite_colours: [u8; 4], -    pub vector: VectorPoints, +    pub palette: [Colour; 16], +    pub sprite_buffer: SpriteBuffer,  }  impl ScreenDevice { @@ -41,25 +54,25 @@ impl ScreenDevice {              foreground: Vec::new(),              background: Vec::new(), -            is_dirty: false, -            is_resizable: true, +            dirty: false, +            resizable: true,              cursor: ScreenPosition::ZERO, +            vector: ScreenPosition::ZERO,              dimensions: ScreenDimensions::ZERO, -            sprite_data: SpriteData::new(), -            palette: [Colour::BLACK; 16], -            palette_high: 0, -            sprite_colours: [0; 4], -            vector: VectorPoints::new(), +            palette_high: 0, +            palette: [Colour::BLACK; 16], +            sprite_buffer: SpriteBuffer::new(),          }      } -    pub fn set_size(&mut self, dimensions: ScreenDimensions) { -        self.is_resizable = false; -        self.resize(dimensions); +    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; @@ -100,19 +113,20 @@ impl ScreenDevice {          };          self.dimensions = dimensions; -        self.is_dirty = true; +        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 palette = [Colour::BLACK; 256]; -        for (i, c) in palette.iter_mut().enumerate() { +        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; @@ -123,8 +137,9 @@ impl ScreenDevice {              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 = palette[(fg << 4 | bg) as usize]; +                *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); @@ -139,17 +154,15 @@ impl ScreenDevice {                      &self.foreground[si..si+width],                  );                  for (b, (bg, fg)) in zip(b_iter, s_iter) { -                    *b = palette[(fg << 4 | bg) as usize]; +                    *b = lookup[(fg << 4 | bg) as usize];                  } -                b_slice[bi+width..bi+width+width_excess].fill(palette[0]); +                b_slice[bi+width..bi+width+width_excess].fill(lookup[0]);                  bi += b_width;                  si += s_width;              } -            b_slice[bi..].fill(palette[0]); +            b_slice[bi..].fill(lookup[0]);          } - -        // Set flags -        self.is_dirty = false; +        self.dirty = false;      }      pub fn set_palette_high(&mut self, val: u8) { @@ -162,24 +175,14 @@ impl ScreenDevice {          let green =                 (val >> 4) * 17;          let blue  =               (val & 0x0f) * 17;          self.palette[index] = Colour::from_rgb(red, green, blue); -        self.is_dirty = true; -    } - -    pub fn set_sprite_colour_high(&mut self, val: u8) { -        self.sprite_colours[0] = val >> 4; -        self.sprite_colours[1] = val & 0x0f; -    } - -    pub fn set_sprite_colour_low(&mut self, val: u8) { -        self.sprite_colours[2] = val >> 4; -        self.sprite_colours[3] = val & 0x0f; +        self.dirty = true;      }      pub fn shunt(&mut self, val: u8) { -        let is_negative = val & 0x80 != 0; -        let is_vertical = val & 0x40 != 0; +        let negative = test!(val, NEGATIVE); +        let vertical = test!(val, VERTICAL);          let dist = (val & 0x3f) as u16; -        match (is_negative, is_vertical) { +        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), @@ -188,31 +191,32 @@ impl ScreenDevice {      }      pub fn draw(&mut self, val: u8) { -        self.vector.push(self.cursor); -        self.is_dirty = true; -        // Parse draw byte -        let draw_mode = val & 0x70; -        let params    = val & 0x0f; +        let operation  = val & 0x70; +        let parameters = val & 0x0f;          let layer = match val & 0x80 != 0 {              true => ScreenLayer::Foreground,              false => ScreenLayer::Background          }; -        match draw_mode { -            0x00 =>       self.draw_pixel(params, layer, self.cursor), -            0x10 => self.draw_sprite_1bit(params, layer), -            0x20 =>       self.fill_layer(params, layer), -            0x30 => self.draw_sprite_2bit(params, layer), -            0x40 =>        self.draw_line(params, layer), -            0x50 =>        self.draw_rect(params, layer), -            0x60 =>    todo!("Draw 1-bit textured line"), -            0x70 =>   self.draw_rect_1bit(params, layer), +        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_rect(parameters, layer), +            0x60 =>   self.draw_line_1bit(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) { return } +        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, @@ -220,6 +224,7 @@ impl ScreenDevice {          };      } +    // 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), @@ -227,227 +232,12 @@ impl ScreenDevice {          }      } -    fn draw_sprite_1bit(&mut self, params: u8, layer: ScreenLayer) { -        let mut sprite = [0; 64]; -        let mut pointer: usize = 0; -        let data = self.sprite_data.get_1bit_sprite(); -        for row in data { -            for x in (0..8).rev() { -                sprite[pointer] = (row >> x) & 0x1; -                pointer += 1; -            } -        } -        self.draw_sprite(params, layer, sprite); -    } - -    fn draw_sprite_2bit(&mut self, params: u8, layer: ScreenLayer) { -        let mut sprite = [0; 64]; -        let mut pointer: usize = 0; -        let data = self.sprite_data.get_2bit_sprite(); -        let (spr1, spr2) = data.split_array_ref::<8>(); -        for (row1, row2) in std::iter::zip(spr1, spr2) { -            for x in (0..8).rev() { -                let bit1 = (row1 >> x << 1) & 0x2; -                let bit2 = (row2 >> x)      & 0x1; -                sprite[pointer] = bit1 | bit2; -                pointer += 1; -            } -        } -        self.draw_sprite(params, layer, sprite); -    } - -    fn draw_line(&mut self, colour: u8, layer: ScreenLayer) { -        let [p0, p1] = self.vector.get_pair(); -        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), -        }; -    } - -    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.vector.get_pair(); - -        // 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 so 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_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 layer = match layer { -                ScreenLayer::Background => &mut self.background, -                ScreenLayer::Foreground => &mut self.foreground, -            }; -            layer[i+x0..=i+x1].fill(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 layer = match layer { -                ScreenLayer::Background => &mut self.background, -                ScreenLayer::Foreground => &mut self.foreground, -            }; -            for _ in y0..=y1 { -                layer[i] = colour; -                i += screen_width; -            } - -        } -    } - -    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 pixels = match layer { -                ScreenLayer::Background => &mut self.background, -                ScreenLayer::Foreground => &mut self.foreground, -            }; -            for _ in y0..=y1 { -                pixels[i..i+rect_width].fill(colour); -                i += screen_width; -            } -        } -    } - -    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 pixels = match layer { -                ScreenLayer::Background => &mut self.background, -                ScreenLayer::Foreground => &mut self.foreground, -            }; - -            let sprite_data = self.sprite_data.get_1bit_sprite(); -            let mut sprite_i = y0 % 8; -            let sprite_x_off = (x0 % 8) as u32; -            let transparent = params & 0x08 != 0; -            if params & 0x07 != 0 { -                todo!("Pre-treat sprite, with rotation/translation"); -            } - -            for _ in y0..=y1 { -                let mut row = sprite_data[sprite_i].rotate_left(sprite_x_off); -                for _ in x0..=x1 { -                    let colour = (row >> 7) as usize; -                    if !(transparent && colour == 0) { -                        pixels[i] = self.sprite_colours[colour]; -                    } -                    row = row.rotate_left(1); -                    i += 1; -                } -                sprite_i = (sprite_i + 1) % 8; -                i += screen_width - rect_width; -            } -        }; -    } - -    fn draw_sprite(&mut self, params: u8, layer: ScreenLayer, sprite: [u8; 64]) { -        let transparent = params & 0x08 != 0; -        let mut position = self.cursor; -        let mut pointer: usize = 0; - -        macro_rules! for8 { ($block:block) => { for _ in 0..8 { $block } }; } -        macro_rules! r   { ($v:expr) => { position.x = position.x.wrapping_add($v) }; } -        macro_rules! l   { ($v:expr) => { position.x = position.x.wrapping_sub($v) }; } -        macro_rules! d   { ($v:expr) => { position.y = position.y.wrapping_add($v) }; } -        macro_rules! u   { ($v:expr) => { position.y = position.y.wrapping_sub($v) }; } -        macro_rules! px { () => { -            let colour = sprite[pointer]; -            if !(transparent && colour == 0) { -                self.draw_pixel(self.sprite_colours[colour as usize], layer, position); -            } -            pointer += 1; -        }; } - - -        match params & 0x07 { -            0 => {               for8!{{ for8!{{ px!(); r!(1); }} l!(8); d!(1); }} } -            1 => { r!(7);        for8!{{ for8!{{ px!(); l!(1); }} r!(8); d!(1); }} } -            2 => {        d!(7); for8!{{ for8!{{ px!(); r!(1); }} l!(8); u!(1); }} } -            3 => { r!(7); d!(7); for8!{{ for8!{{ px!(); l!(1); }} r!(8); u!(1); }} } - -            4 => {               for8!{{ for8!{{ px!(); d!(1); }} u!(8); r!(1); }} } -            5 => { r!(7);        for8!{{ for8!{{ px!(); d!(1); }} u!(8); l!(1); }} } -            6 => {         d!(7);for8!{{ for8!{{ px!(); u!(1); }} d!(8); r!(1); }} } -            7 => { r!(7); d!(7); for8!{{ for8!{{ px!(); u!(1); }} d!(8); l!(1); }} } - -            _ => unreachable!(), -        } -    } - -    /// Returns [x0, y0, x1, y1] +    /// 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.vector.get_pair(); +        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) ]; 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 -    } -} diff --git a/src/devices/system.rs b/src/devices/system.rs new file mode 100644 index 0000000..6eb9510 --- /dev/null +++ b/src/devices/system.rs @@ -0,0 +1,4 @@ +mod read_only_text_buffer; + +pub use read_only_text_buffer::ReadOnlyTextBuffer; + diff --git a/src/devices/system/read_only_text_buffer.rs b/src/devices/system/read_only_text_buffer.rs new file mode 100644 index 0000000..7c59025 --- /dev/null +++ b/src/devices/system/read_only_text_buffer.rs @@ -0,0 +1,23 @@ +pub struct ReadOnlyTextBuffer { +    chars: Vec<u8>, +    pointer: usize, +} + +impl ReadOnlyTextBuffer { +    pub fn from_text(text: &str) -> Self { +        Self { +            chars: text.chars().map(|c| c as u32 as u8).collect(), +            pointer: 0, +        } +    } + +    pub fn read_byte(&mut self) -> u8 { +        let byte = self.chars[self.pointer]; +        self.pointer += 1; +        return byte; +    } + +    pub fn reset_pointer(&mut self) { +        self.pointer = 0; +    } +} | 
