1#![no_std]
51#![cfg_attr(feature = "const_fn", feature(const_fn))]
52
53extern crate console_traits;
60#[macro_use]
61extern crate const_ft;
62
63mod charset;
70pub mod freebsd_cp850;
71pub mod freebsd_teletext;
72mod maps;
73
74pub use charset::*;
81pub use console_traits::*;
82use core::sync::atomic::{AtomicUsize, Ordering};
83use maps::RGB_MAPS;
84
85pub const MODE0_USABLE_LINES: usize = 576;
93pub const MODE0_USABLE_COLS: usize = 384;
95pub const MODE0_HORIZONTAL_OCTETS: usize = 50;
97pub const MODE0_USABLE_HORIZONTAL_OCTETS: usize = 48;
99pub const MODE0_TEXT_NUM_COLS: usize = MODE0_USABLE_COLS / MAX_FONT_WIDTH;
101pub const MODE0_TEXT_MAX_COL: usize = MODE0_TEXT_NUM_COLS - 1;
103pub const MODE0_TEXT_NUM_ROWS: usize = MODE0_USABLE_LINES / MAX_FONT_HEIGHT;
105pub const MODE0_TEXT_MAX_ROW: usize = MODE0_TEXT_NUM_ROWS - 1;
107
108pub const MODE2_WIDTH_PIXELS: usize = 384;
110pub const MODE2_USABLE_LINES: usize = 288;
112
113const H_VISIBLE_AREA_20MHZ: u32 = 400;
122const H_FRONT_PORCH_20MHZ: u32 = 20;
123const H_SYNC_PULSE_20MHZ: u32 = 64;
124const H_BACK_PORCH_20MHZ: u32 = 44;
125const H_WHOLE_LINE_20MHZ: u32 =
126 H_VISIBLE_AREA_20MHZ + H_FRONT_PORCH_20MHZ + H_SYNC_PULSE_20MHZ + H_BACK_PORCH_20MHZ;
127const V_VISIBLE_AREA: usize = 600;
135const V_FRONT_PORCH: usize = 1;
136const V_SYNC_PULSE: usize = 4;
137const V_BACK_PORCH: usize = 23;
138const V_WHOLE_FRAME: usize = V_SYNC_PULSE + V_BACK_PORCH + V_VISIBLE_AREA + V_FRONT_PORCH;
139const V_TOP_BORDER: usize = 12;
140const V_BOTTOM_BORDER: usize = 12;
141
142const MAX_FONT_HEIGHT: usize = 16;
143const MAX_FONT_WIDTH: usize = 8;
144const V_SYNC_PULSE_FIRST: usize = 0;
145const V_BACK_PORCH_FIRST: usize = V_SYNC_PULSE_FIRST + V_SYNC_PULSE;
146const V_TOP_BORDER_FIRST: usize = V_BACK_PORCH_FIRST + V_BACK_PORCH;
147const V_TOP_BORDER_LAST: usize = V_DATA_FIRST - 1;
148const V_DATA_FIRST: usize = V_TOP_BORDER_FIRST + V_TOP_BORDER;
149const V_DATA_LAST: usize = V_BOTTOM_BORDER_FIRST - 1;
150const V_BOTTOM_BORDER_FIRST: usize = V_DATA_FIRST + (MAX_FONT_HEIGHT * MODE0_TEXT_NUM_ROWS);
151const V_BOTTOM_BORDER_LAST: usize = V_FRONT_PORCH_FIRST - 1;
152const V_FRONT_PORCH_FIRST: usize = V_BOTTOM_BORDER_FIRST + V_BOTTOM_BORDER;
153
154const DEFAULT_ATTR: Attr = Attr::new(Colour::White, Colour::Blue);
156
157const CURSOR: Char = Char::LowLine;
158
159pub trait Hardware {
167 fn configure(&mut self, mode_info: &ModeInfo);
187
188 fn vsync_on(&mut self);
190
191 fn vsync_off(&mut self);
193
194 fn write_pixels(&mut self, xrgb: XRGBColour);
196}
197
198trait VideoMode {
206 fn octets(&self) -> usize;
208 fn clock_speed(&self) -> u32;
210}
211
212#[derive(Debug)]
220pub struct ModeInfo {
221 pub width: u32,
223 pub visible_width: u32,
225 pub sync_end: u32,
227 pub line_start: u32,
230 pub clock_rate: u32,
232 pub num_lines: u32,
234 pub visible_lines: u32,
236}
237
238#[repr(C)]
245pub struct FrameBuffer<T>
246where
247 T: Hardware,
248{
249 line_no: AtomicUsize,
250 frame: usize,
251 text_buffer: [Mode0TextRow; MODE0_TEXT_NUM_ROWS + 1],
253 roller_buffer: [u16; MODE0_USABLE_LINES],
255 hw: Option<T>,
256 attr: Attr,
257 pos: Position,
258 mode: ControlCharMode,
259 escape_mode: EscapeCharMode,
260 mode2: Option<Mode2>,
261 font: Option<*const u8>,
262 cursor_visible: bool,
263 under_cursor: Char,
264}
265
266#[derive(Copy, Clone, Eq, PartialEq, Debug)]
269pub struct Attr(u8);
270
271#[derive(Copy, Clone, Eq, PartialEq, Debug)]
272pub enum Colour {
273 White = 7,
274 Yellow = 6,
275 Magenta = 5,
276 Red = 4,
277 Cyan = 3,
278 Green = 2,
279 Blue = 1,
280 Black = 0,
281}
282
283#[derive(Debug, Copy, Clone)]
285pub struct XRGBColour(pub u32);
286
287pub struct Mode2 {
289 buffer: *const u8,
290 start: usize,
291 end: usize,
292}
293
294#[derive(Copy, Clone, Eq, PartialEq, Debug)]
297pub struct Point(pub usize, pub usize);
298
299#[derive(Copy, Clone, Eq, PartialEq, Debug)]
302pub enum DoubleHeightMode {
303 Normal,
304 Top,
305 Bottom,
306}
307
308#[derive(Copy, Clone)]
309pub struct Mode0TextRow {
310 pub double_height: DoubleHeightMode,
311 pub glyphs: [(Char, Attr); MODE0_TEXT_NUM_COLS],
312}
313
314impl<T> FrameBuffer<T>
329where
330 T: Hardware,
331{
332 const_ft! {
334 pub fn new() -> FrameBuffer<T> {
336 FrameBuffer {
337 line_no: AtomicUsize::new(0),
338 frame: 0,
339 text_buffer: [Mode0TextRow {
340 double_height: DoubleHeightMode::Normal,
341 glyphs: [(Char::Null, DEFAULT_ATTR); MODE0_TEXT_NUM_COLS],
342 }; MODE0_TEXT_NUM_ROWS + 1],
343 roller_buffer: [0; MODE0_USABLE_LINES],
344 hw: None,
345 pos: Position {
346 row: Row(0),
347 col: Col(0),
348 },
349 attr: DEFAULT_ATTR,
350 mode: ControlCharMode::Interpret,
351 escape_mode: EscapeCharMode::Waiting,
352 mode2: None,
353 font: None,
354 cursor_visible: true,
355 under_cursor: Char::Space,
356 }
357 }
358 }
359
360 pub fn init(&mut self, mut hw: T) {
362 assert_eq!(MAX_FONT_WIDTH, 8);
364 let mode_info = ModeInfo {
365 width: H_WHOLE_LINE_20MHZ,
367 visible_width: H_VISIBLE_AREA_20MHZ,
369 sync_end: H_SYNC_PULSE_20MHZ,
372 line_start: H_SYNC_PULSE_20MHZ + H_BACK_PORCH_20MHZ,
375 clock_rate: 20_000_000,
377 num_lines: V_WHOLE_FRAME as u32,
379 visible_lines: V_VISIBLE_AREA as u32,
381 };
382
383 hw.configure(&mode_info);
384 self.hw = Some(hw);
385 for (idx, line) in self.roller_buffer.iter_mut().enumerate() {
386 *line = idx as u16;
387 }
388 self.clear();
389 }
390
391 pub fn borrow_hw_mut(&mut self) -> Option<&mut T> {
392 if let Some(x) = &mut self.hw {
393 Some(x)
394 } else {
395 None
396 }
397 }
398
399 pub fn borrow_hw(&self) -> Option<&T> {
400 if let Some(x) = &self.hw {
401 Some(x)
402 } else {
403 None
404 }
405 }
406
407 pub fn set_cursor_visible(&mut self, visible: bool) {
408 if visible != self.cursor_visible {
409 if visible {
410 self.cursor_visible = true;
411 self.under_cursor = self.current_cell().0;
412 self.current_cell().0 = CURSOR;
413 } else {
414 self.cursor_visible = false;
415 self.current_cell().0 = self.under_cursor;
416 }
417 }
418 }
419
420 pub fn mode2(&mut self, buffer: &[u8], start_line: usize) {
425 let length = buffer.len();
426 let buffer_lines = length / MODE0_USABLE_HORIZONTAL_OCTETS;
427 let mode2 = Mode2 {
428 buffer: buffer.as_ptr(),
429 start: start_line,
430 end: start_line + (2 * buffer_lines),
432 };
433 self.mode2 = Some(mode2);
434 }
435
436 pub fn mode2_shift(&mut self, new_start_line: usize) {
437 if let Some(mode2) = self.mode2.as_mut() {
438 mode2.start = new_start_line;
439 }
440 }
441
442 pub fn mode2_release(&mut self) {
445 let mut mode2_opt = None;
446 core::mem::swap(&mut self.mode2, &mut mode2_opt);
447 }
448
449 pub fn map_line(&mut self, visible_line: u16, rendered_line: u16) {
450 if (rendered_line as usize) < MODE0_USABLE_LINES {
451 if let Some(n) = self.roller_buffer.get_mut(visible_line as usize) {
452 *n = rendered_line;
453 }
454 }
455 }
456
457 pub fn frame(&self) -> usize {
459 self.frame
460 }
461
462 pub fn line(&self) -> Option<usize> {
464 let line = self.line_no.load(Ordering::Relaxed);
465 if line >= V_DATA_FIRST && line <= V_DATA_LAST {
466 Some(line - V_DATA_FIRST)
467 } else {
468 None
469 }
470 }
471
472 pub fn total_line(&self) -> u64 {
474 let line_a = self.line_no.load(Ordering::Relaxed);
475 let mut f = self.frame;
476 let line_b = self.line_no.load(Ordering::Relaxed);
477 if line_b < line_a {
478 f = self.frame;
480 }
481 ((f as u64) * (V_WHOLE_FRAME as u64)) + (line_b as u64)
482 }
483
484 pub fn isr_sol(&mut self) {
486 match self.line_no.load(Ordering::Relaxed) {
487 V_BOTTOM_BORDER_FIRST..=V_BOTTOM_BORDER_LAST => {
488 self.solid_line();
489 }
490 V_TOP_BORDER_FIRST..=V_TOP_BORDER_LAST => {
491 self.solid_line();
492 }
493 V_DATA_FIRST..=V_DATA_LAST => {
494 self.calculate_pixels();
495 }
496 V_BACK_PORCH_FIRST => {
497 if let Some(ref mut hw) = self.hw {
498 hw.vsync_off();
499 }
500 }
501 V_FRONT_PORCH_FIRST => {
502 self.frame = self.frame.wrapping_add(1);
504 }
505 V_WHOLE_FRAME => {
506 self.line_no.store(0, Ordering::Relaxed);
508 if let Some(ref mut hw) = self.hw {
509 hw.vsync_on();
510 }
511 }
512 _ => {
513 }
515 }
516 self.line_no.fetch_add(1, Ordering::Relaxed);
517 }
518
519 fn solid_line(&mut self) {
522 if let Some(ref mut hw) = self.hw {
523 for _ in 0..MODE0_HORIZONTAL_OCTETS {
525 hw.write_pixels(XRGBColour::new(0xFF, 0xFF, 0xFF));
526 }
527 }
528 }
529
530 fn calculate_pixels(&mut self) {
535 let real_line = self.line_no.load(Ordering::Relaxed) - V_DATA_FIRST;
536 let line = self.roller_buffer[real_line as usize] as usize;
537 let text_row = line / MAX_FONT_HEIGHT;
538 let row = &self.text_buffer[text_row];
539 let font_row = match row.double_height {
540 DoubleHeightMode::Normal => line % MAX_FONT_HEIGHT,
541 DoubleHeightMode::Top => (line % MAX_FONT_HEIGHT) / 2,
542 DoubleHeightMode::Bottom => ((line % MAX_FONT_HEIGHT) + MAX_FONT_HEIGHT) / 2,
543 };
544 let font_table = self
545 .font
546 .unwrap_or_else(|| freebsd_cp850::FONT_DATA.as_ptr());
547 if let Some(ref mut hw) = self.hw {
548 hw.write_pixels(XRGBColour::new(0xFF, 0xFF, 0xFF));
550
551 let mut need_text = true;
552 if let Some(mode2) = self.mode2.as_ref() {
553 if line >= mode2.start && line < mode2.end && text_row < MODE0_TEXT_NUM_ROWS {
554 let framebuffer_line = (line - mode2.start) >> 1;
558
559 let start = framebuffer_line * MODE0_USABLE_HORIZONTAL_OCTETS;
561 let framebuffer_offsets = (start as isize)
562 ..(start as isize + MODE0_USABLE_HORIZONTAL_OCTETS as isize);
563
564 for ((_, attr), framebuffer_offset) in self.text_buffer[text_row]
566 .glyphs
567 .iter()
568 .zip(framebuffer_offsets)
569 {
570 let w = unsafe { *mode2.buffer.offset(framebuffer_offset) };
571 let rgb_addr = unsafe {
576 RGB_MAPS
577 .as_ptr()
578 .offset(((attr.0 as isize) * 256_isize) + (w as isize))
579 };
580 let rgb_word = unsafe { *rgb_addr };
581 hw.write_pixels(rgb_word);
582 }
583 need_text = false;
584 }
585 }
586
587 if need_text {
588 let font_table = unsafe { font_table.add(font_row) };
590 for (ch, attr) in row.glyphs.iter() {
591 let index = (*ch as isize) * (MAX_FONT_HEIGHT as isize);
592 let mono_pixels = unsafe { *font_table.offset(index) };
593 let rgb_addr = unsafe {
598 RGB_MAPS
599 .as_ptr()
600 .offset(((attr.0 as isize) * 256_isize) + (mono_pixels as isize))
601 };
602 let rgb_word = unsafe { *rgb_addr };
603 hw.write_pixels(rgb_word);
604 }
605 }
606
607 hw.write_pixels(XRGBColour::new(0xFF, 0xFF, 0xFF));
609 }
610 }
611
612 pub fn set_custom_font(&mut self, new_font: Option<&'static [u8]>) {
614 self.font = match new_font {
615 Some(x) => {
617 assert_eq!(x.len(), 256 * MAX_FONT_HEIGHT);
618 Some(x.as_ptr())
619 }
620 None => None,
622 };
623 }
624
625 pub fn clear(&mut self) {
627 for row in self.text_buffer.iter_mut() {
628 for slot in row.glyphs.iter_mut() {
629 *slot = (Char::Space, self.attr);
630 }
631 row.double_height = DoubleHeightMode::Normal;
632 }
633 self.pos = Position::origin();
634 }
635
636 pub fn write_glyph_at(&mut self, glyph: Char, pos: Position, attr: Option<Attr>) {
638 if self.cursor_visible && (pos.row == self.pos.row) && (pos.col == self.pos.col) {
639 self.under_cursor = glyph;
640 self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize].1 =
641 attr.unwrap_or(self.attr);
642 } else if (pos.col <= self.get_width()) && (pos.row <= self.get_height()) {
643 self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize] =
644 (glyph, attr.unwrap_or(self.attr));
645 }
646 }
647
648 pub fn read_glyph_at(&mut self, pos: Position) -> Option<(Char, Attr)> {
650 if self.cursor_visible && (pos.row == self.pos.row) && (pos.col == self.pos.col) {
651 Some((
652 self.under_cursor,
653 self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize].1,
654 ))
655 } else if (pos.col <= self.get_width()) && (pos.row <= self.get_height()) {
656 Some(self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize])
657 } else {
658 None
659 }
660 }
661
662 pub fn write_glyph(&mut self, glyph: Char, attr: Option<Attr>) {
664 if self.cursor_visible {
665 self.under_cursor = glyph;
666 self.current_cell().1 = attr.unwrap_or(self.attr);
667 } else {
668 *self.current_cell() = (glyph, attr.unwrap_or(self.attr));
669 }
670 self.move_cursor_right().unwrap();
671 }
672
673 pub fn write_char(&mut self, ch: u8, attr: Option<Attr>) {
675 self.write_glyph(Char::from_byte(ch), attr);
676 }
677
678 pub fn set_attr_at(&mut self, pos: Position, attr: Attr) {
680 self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize].1 = attr;
681 }
682
683 pub fn set_line_mode_at(&mut self, row: Row, double_height: DoubleHeightMode) {
685 self.text_buffer[row.0 as usize].double_height = double_height;
686 }
687
688 pub fn set_line_mode(&mut self, double_height: DoubleHeightMode) {
690 self.text_buffer[self.pos.row.0 as usize].double_height = double_height;
691 }
692
693 pub fn set_attr(&mut self, attr: Attr) -> Attr {
695 let old = self.attr;
696 self.attr = attr;
697 old
698 }
699
700 pub fn get_attr(&mut self) -> Attr {
702 self.attr
703 }
704
705 fn current_cell(&mut self) -> &mut (Char, Attr) {
706 &mut self.text_buffer[self.pos.row.0 as usize].glyphs[self.pos.col.0 as usize]
707 }
708}
709
710impl<T> BaseConsole for FrameBuffer<T>
711where
712 T: Hardware,
713{
714 type Error = ();
715
716 fn get_width(&self) -> Col {
718 Col(MODE0_TEXT_MAX_COL as u8)
719 }
720
721 fn get_height(&self) -> Row {
723 Row(MODE0_TEXT_MAX_ROW as u8)
724 }
725
726 fn set_col(&mut self, col: Col) -> Result<(), Self::Error> {
728 if col <= self.get_width() {
729 if self.cursor_visible {
730 self.current_cell().0 = self.under_cursor;
731 self.pos.col = col;
732 self.under_cursor = self.current_cell().0;
733 self.current_cell().0 = CURSOR;
734 } else {
735 self.pos.col = col;
736 }
737 Ok(())
738 } else {
739 Err(())
740 }
741 }
742
743 fn set_row(&mut self, row: Row) -> Result<(), Self::Error> {
745 if row <= self.get_height() {
746 if self.cursor_visible {
747 self.current_cell().0 = self.under_cursor;
748 self.pos.row = row;
749 self.under_cursor = self.current_cell().0;
750 self.current_cell().0 = CURSOR;
751 } else {
752 self.pos.row = row;
753 }
754 Ok(())
755 } else {
756 Err(())
757 }
758 }
759
760 fn set_pos(&mut self, pos: Position) -> Result<(), Self::Error> {
762 if pos.row <= self.get_height() && pos.col <= self.get_width() {
763 if self.cursor_visible {
764 self.current_cell().0 = self.under_cursor;
765 self.pos = pos;
766 self.under_cursor = self.current_cell().0;
767 self.current_cell().0 = CURSOR;
768 } else {
769 self.pos = pos;
770 }
771 Ok(())
772 } else {
773 Err(())
774 }
775 }
776
777 fn get_pos(&self) -> Position {
779 self.pos
780 }
781
782 fn set_control_char_mode(&mut self, mode: ControlCharMode) {
784 self.mode = mode;
785 }
786
787 fn get_control_char_mode(&self) -> ControlCharMode {
789 self.mode
790 }
791
792 fn set_escape_char_mode(&mut self, mode: EscapeCharMode) {
794 self.escape_mode = mode;
795 }
796
797 fn get_escape_char_mode(&self) -> EscapeCharMode {
799 self.escape_mode
800 }
801
802 fn scroll_screen(&mut self) -> Result<(), Self::Error> {
804 let old_cursor = self.cursor_visible;
805 self.set_cursor_visible(false);
806 for line in 0..MODE0_TEXT_NUM_ROWS - 1 {
807 self.text_buffer[line] = self.text_buffer[line + 1];
808 }
809 for slot in self.text_buffer[MODE0_TEXT_MAX_ROW].glyphs.iter_mut() {
810 *slot = (Char::Space, self.attr);
811 }
812 self.set_cursor_visible(old_cursor);
813 Ok(())
814 }
815}
816
817impl<T> AsciiConsole for FrameBuffer<T>
818where
819 T: Hardware,
820{
821 fn handle_escape(&mut self, escaped_char: u8) -> bool {
825 match escaped_char {
826 b'W' => {
827 self.attr.set_fg(Colour::White);
828 }
829 b'Y' => {
830 self.attr.set_fg(Colour::Yellow);
831 }
832 b'M' => {
833 self.attr.set_fg(Colour::Magenta);
834 }
835 b'R' => {
836 self.attr.set_fg(Colour::Red);
837 }
838 b'C' => {
839 self.attr.set_fg(Colour::Cyan);
840 }
841 b'G' => {
842 self.attr.set_fg(Colour::Green);
843 }
844 b'B' => {
845 self.attr.set_fg(Colour::Blue);
846 }
847 b'K' => {
848 self.attr.set_fg(Colour::Black);
849 }
850 b'w' => {
851 self.attr.set_bg(Colour::White);
852 }
853 b'y' => {
854 self.attr.set_bg(Colour::Yellow);
855 }
856 b'm' => {
857 self.attr.set_bg(Colour::Magenta);
858 }
859 b'r' => {
860 self.attr.set_bg(Colour::Red);
861 }
862 b'c' => {
863 self.attr.set_bg(Colour::Cyan);
864 }
865 b'g' => {
866 self.attr.set_bg(Colour::Green);
867 }
868 b'b' => {
869 self.attr.set_bg(Colour::Blue);
870 }
871 b'k' => {
872 self.attr.set_bg(Colour::Black);
873 }
874 b'^' => {
875 self.set_line_mode(DoubleHeightMode::Top);
876 }
877 b'v' => {
878 self.set_line_mode(DoubleHeightMode::Bottom);
879 }
880 b'-' => {
881 self.set_line_mode(DoubleHeightMode::Normal);
882 }
883 b'Z' => {
884 self.clear();
885 }
886 _ => {}
887 }
888 true
890 }
891
892 fn write_char_at(&mut self, ch: u8, pos: Position) -> Result<(), Self::Error> {
895 if self.cursor_visible && (pos.row == self.pos.row) && (pos.col == self.pos.col) {
896 self.under_cursor = Char::from_byte(ch);
897 self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize].1 = self.attr;
898 } else if (pos.col <= self.get_width()) && (pos.row <= self.get_height()) {
899 self.text_buffer[pos.row.0 as usize].glyphs[pos.col.0 as usize] =
900 (Char::from_byte(ch), self.attr);
901 }
902 Ok(())
903 }
904}
905
906impl<T> core::fmt::Write for FrameBuffer<T>
907where
908 T: Hardware,
909{
910 fn write_str(&mut self, s: &str) -> core::fmt::Result {
911 for ch in s.chars() {
912 self.write_character(Char::map_char(ch) as u8)
913 .map_err(|_| core::fmt::Error)?;
914 }
915 Ok(())
916 }
917}
918
919impl Attr {
920 const FG_BITS: u8 = 0b0011_1000;
921 const BG_BITS: u8 = 0b0000_0111;
922
923 pub const fn new(fg: Colour, bg: Colour) -> Attr {
924 Attr(((fg as u8) << 3) + (bg as u8))
925 }
926
927 pub fn set_fg(&mut self, fg: Colour) -> &mut Attr {
928 self.0 = ((fg as u8) << 3) + (self.0 & !Self::FG_BITS);
929 self
930 }
931
932 pub fn set_bg(&mut self, bg: Colour) -> &mut Attr {
933 self.0 = (self.0 & !Self::BG_BITS) + (bg as u8);
934 self
935 }
936
937 pub fn as_u8(self) -> u8 {
938 self.0
939 }
940}
941
942impl core::default::Default for Attr {
943 fn default() -> Self {
944 DEFAULT_ATTR
945 }
946}
947
948impl Colour {
949 pub fn into_pixels(self) -> XRGBColour {
951 match self {
952 Colour::White => XRGBColour::new(0xFF, 0xFF, 0xFF),
953 Colour::Yellow => XRGBColour::new(0xFF, 0xFF, 0x00),
954 Colour::Magenta => XRGBColour::new(0xFF, 0x00, 0xFF),
955 Colour::Red => XRGBColour::new(0xFF, 0x00, 0x00),
956 Colour::Cyan => XRGBColour::new(0x00, 0xFF, 0xFF),
957 Colour::Green => XRGBColour::new(0x00, 0xFF, 0x00),
958 Colour::Blue => XRGBColour::new(0x00, 0x00, 0xFF),
959 Colour::Black => XRGBColour::new(0x00, 0x00, 0x00),
960 }
961 }
962}
963
964impl XRGBColour {
965 pub const fn new(red: u8, green: u8, blue: u8) -> XRGBColour {
968 XRGBColour(((red as u32) << 16) | ((green as u32) << 8) | (blue as u32))
969 }
970
971 pub const fn red(self) -> u32 {
973 (self.0 >> 16) & 0xFF
974 }
975
976 pub const fn green(self) -> u32 {
978 (self.0 >> 8) & 0xFF
979 }
980
981 pub const fn blue(self) -> u32 {
983 self.0 & 0xFF
984 }
985
986 pub const fn pixel_has_red(self, pixel: u8) -> bool {
988 ((self.0 >> (16 + (7 - pixel))) & 1) == 1
989 }
990
991 pub const fn pixel_has_green(self, pixel: u8) -> bool {
993 ((self.0 >> (8 + (7 - pixel))) & 1) == 1
994 }
995
996 pub const fn pixel_has_blue(self, pixel: u8) -> bool {
998 ((self.0 >> (7 - pixel)) & 1) == 1
999 }
1000}
1001
1002