diff --git a/src/cp437.rs b/src/cp437.rs index 400cb9c..775a448 100644 --- a/src/cp437.rs +++ b/src/cp437.rs @@ -35,7 +35,7 @@ pub const GLYPHS: [[u8; 14]; 256] = [ [0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00], // [↔] (29) [0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00], // [▲] (30) [0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00], // [▼] (31) - [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // [ ] (32) + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // [SP] (32) [0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00], // [!] (33) [0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // ["] (34) [0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00], // [#] (35) @@ -258,12 +258,12 @@ pub const GLYPHS: [[u8; 14]; 256] = [ [0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // [ⁿ] (252) [0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // [²] (253) [0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00], // [■] (254) - [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // [ ] (255) + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // [NBSP] (255) ]; /// Prints a specific glyph to StdOut for easier viewing. #[allow(dead_code)] -fn debug_print_glyph(glyph: [u8; 14]) { +pub fn debug_print_glyph(glyph: [u8; 14]) { for line in glyph.iter() { let bits = line.view_bits::(); for pixel in bits.iter() { diff --git a/src/ega.rs b/src/ega.rs new file mode 100644 index 0000000..a5ef6d9 --- /dev/null +++ b/src/ega.rs @@ -0,0 +1,145 @@ +extern crate sdl2; + +use sdl2::pixels::Color; + +pub enum EgaColors { + Black, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + LightGray, + DarkGray, + BrightBlue, + BrightGreen, + BrightCyan, + BrightRed, + BrightMagenta, + BrightYellow, + White, +} + +impl TryFrom for EgaColors { + type Error = &'static str; + + fn try_from(num: u8) -> Result { + match num { + 0 => Ok(EgaColors::Black), + 1 => Ok(EgaColors::Blue), + 2 => Ok(EgaColors::Green), + 3 => Ok(EgaColors::Cyan), + 4 => Ok(EgaColors::Red), + 5 => Ok(EgaColors::Magenta), + 6 => Ok(EgaColors::Brown), + 7 => Ok(EgaColors::LightGray), + 8 => Ok(EgaColors::DarkGray), + 9 => Ok(EgaColors::BrightBlue), + 10 => Ok(EgaColors::BrightGreen), + 11 => Ok(EgaColors::BrightCyan), + 12 => Ok(EgaColors::BrightRed), + 13 => Ok(EgaColors::BrightMagenta), + 14 => Ok(EgaColors::BrightYellow), + 15 => Ok(EgaColors::White), + _ => Err("EgaColors must be between 0 and 15 inclusive."), + } + } +} + +impl Into for EgaColors { + fn into(self) -> u8 { + match self { + EgaColors::Black => 0, + EgaColors::Blue => 1, + EgaColors::Green => 2, + EgaColors::Cyan => 3, + EgaColors::Red => 4, + EgaColors::Magenta => 5, + EgaColors::Brown => 6, + EgaColors::LightGray => 7, + EgaColors::DarkGray => 8, + EgaColors::BrightBlue => 9, + EgaColors::BrightGreen => 10, + EgaColors::BrightCyan => 11, + EgaColors::BrightRed => 12, + EgaColors::BrightMagenta => 13, + EgaColors::BrightYellow => 14, + EgaColors::White => 15, + } + } +} + +impl TryFrom for EgaColors { + type Error = &'static str; + + fn try_from(color: Color) -> Result { + match color { + Color { r: 0x00, g: 0x00, b: 0x00, a: 0xff } => Ok(EgaColors::Black), + Color { r: 0x00, g: 0x00, b: 0xaa, a: 0xff } => Ok(EgaColors::Blue), + Color { r: 0x00, g: 0xaa, b: 0x00, a: 0xff } => Ok(EgaColors::Green), + Color { r: 0x00, g: 0xaa, b: 0xaa, a: 0xff } => Ok(EgaColors::Cyan), + Color { r: 0xaa, g: 0x00, b: 0x00, a: 0xff } => Ok(EgaColors::Red), + Color { r: 0xaa, g: 0x00, b: 0xaa, a: 0xff } => Ok(EgaColors::Magenta), + Color { r: 0xaa, g: 0x55, b: 0x00, a: 0xff } => Ok(EgaColors::Brown), + Color { r: 0xaa, g: 0xaa, b: 0xaa, a: 0xff } => Ok(EgaColors::LightGray), + Color { r: 0x55, g: 0x55, b: 0x55, a: 0xff } => Ok(EgaColors::DarkGray), + Color { r: 0x55, g: 0x55, b: 0xff, a: 0xff } => Ok(EgaColors::BrightBlue), + Color { r: 0x55, g: 0xff, b: 0x55, a: 0xff } => Ok(EgaColors::BrightGreen), + Color { r: 0x55, g: 0xff, b: 0xff, a: 0xff } => Ok(EgaColors::BrightCyan), + Color { r: 0xff, g: 0x55, b: 0x55, a: 0xff } => Ok(EgaColors::BrightRed), + Color { r: 0xff, g: 0x55, b: 0xff, a: 0xff } => Ok(EgaColors::BrightMagenta), + Color { r: 0xff, g: 0xff, b: 0x55, a: 0xff } => Ok(EgaColors::BrightYellow), + Color { r: 0xff, g: 0xff, b: 0xff, a: 0xff } => Ok(EgaColors::White), + _ => Err("The RGB color provided does not match an EgaColor."), + } + } +} + +impl Into for EgaColors { + fn into(self) -> Color { + match self { + EgaColors::Black => Color { r: 0x00, g: 0x00, b: 0x00, a: 0xff }, + EgaColors::Blue => Color { r: 0x00, g: 0x00, b: 0xaa, a: 0xff }, + EgaColors::Green => Color { r: 0x00, g: 0xaa, b: 0x00, a: 0xff }, + EgaColors::Cyan => Color { r: 0x00, g: 0xaa, b: 0xaa, a: 0xff }, + EgaColors::Red => Color { r: 0xaa, g: 0x00, b: 0x00, a: 0xff }, + EgaColors::Magenta => Color { r: 0xaa, g: 0x00, b: 0xaa, a: 0xff }, + EgaColors::Brown => Color { r: 0xaa, g: 0x55, b: 0x00, a: 0xff }, + EgaColors::LightGray => Color { r: 0xaa, g: 0xaa, b: 0xaa, a: 0xff }, + EgaColors::DarkGray => Color { r: 0x55, g: 0x55, b: 0x55, a: 0xff }, + EgaColors::BrightBlue => Color { r: 0x55, g: 0x55, b: 0xff, a: 0xff }, + EgaColors::BrightGreen => Color { r: 0x55, g: 0xff, b: 0x55, a: 0xff }, + EgaColors::BrightCyan => Color { r: 0x55, g: 0xff, b: 0xff, a: 0xff }, + EgaColors::BrightRed => Color { r: 0xff, g: 0x55, b: 0x55, a: 0xff }, + EgaColors::BrightMagenta => Color { r: 0xff, g: 0x55, b: 0xff, a: 0xff }, + EgaColors::BrightYellow => Color { r: 0xff, g: 0xff, b: 0x55, a: 0xff }, + EgaColors::White => Color { r: 0xff, g: 0xff, b: 0xff, a: 0xff }, + } + } +} + +impl EgaColors { + pub fn u8_to_rgb(num: u8) -> Color { + let col = match num { + 0 => Color::RGB(0x00, 0x00, 0x00), + 1 => Color::RGB(0x00, 0x00, 0xaa), + 2 => Color::RGB(0x00, 0xaa, 0x00), + 3 => Color::RGB(0x00, 0xaa, 0xaa), + 4 => Color::RGB(0xaa, 0x00, 0x00), + 5 => Color::RGB(0xaa, 0x00, 0xaa), + 6 => Color::RGB(0xaa, 0x55, 0x00), + 7 => Color::RGB(0xaa, 0xaa, 0xaa), + 8 => Color::RGB(0x55, 0x55, 0x55), + 9 => Color::RGB(0x55, 0x55, 0xff), + 10 => Color::RGB(0x55, 0xff, 0x55), + 11 => Color::RGB(0x55, 0xff, 0xff), + 12 => Color::RGB(0xff, 0x55, 0x55), + 13 => Color::RGB(0xff, 0x55, 0xff), + 14 => Color::RGB(0xff, 0xff, 0x55), + 15 => Color::RGB(0xff, 0xff, 0xff), + _ => panic!("Unknown value for EgaColors: {}", num), + }; + col + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 161da4f..4f735cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,9 @@ use sdl2::rect::Rect; use sdl2::render::{WindowCanvas, Texture}; pub mod cp437; +pub mod ega; use cp437::GLYPHS; +use ega::EgaColors; const SCREEN_HEIGHT: u32 = 350; const SCREEN_WIDTH: u32 = 640; @@ -31,94 +33,6 @@ const TOTAL_SCREEN_BLOCKS: usize = 2000; const FPS_60_HERTZ: u32 = 1_000_000_000u32 / 60; const BLINK_RATE: u128 = 266666666; -pub enum EgaColors { - Black, - Blue, - Green, - Cyan, - Red, - Magenta, - Brown, - LightGray, - DarkGray, - BrightBlue, - BrightGreen, - BrightCyan, - BrightRed, - BrightMagenta, - BrightYellow, - White, -} - -impl EgaColors { - pub fn as_u8(color: EgaColors) -> u8 { - let num = match color { - EgaColors::Black => 0, - EgaColors::Blue => 1, - EgaColors::Green => 2, - EgaColors::Cyan => 3, - EgaColors::Red => 4, - EgaColors::Magenta => 5, - EgaColors::Brown => 6, - EgaColors::LightGray => 7, - EgaColors::DarkGray => 8, - EgaColors::BrightBlue => 9, - EgaColors::BrightGreen => 10, - EgaColors::BrightCyan => 11, - EgaColors::BrightRed => 12, - EgaColors::BrightMagenta => 13, - EgaColors::BrightYellow => 14, - EgaColors::White => 15, - }; - num - } - - pub fn from_enum(color: EgaColors) -> Color { - let col = match color { - EgaColors::Black => Color::RGB(0x00, 0x00, 0x00), - EgaColors::Blue => Color::RGB(0x00, 0x00, 0xaa), - EgaColors::Green => Color::RGB(0x00, 0xaa, 0x00), - EgaColors::Cyan => Color::RGB(0x00, 0xaa, 0xaa), - EgaColors::Red => Color::RGB(0xaa, 0x00, 0x00), - EgaColors::Magenta => Color::RGB(0xaa, 0x00, 0xaa), - EgaColors::Brown => Color::RGB(0xaa, 0x55, 0x00), - EgaColors::LightGray => Color::RGB(0xaa, 0xaa, 0xaa), - EgaColors::DarkGray => Color::RGB(0x55, 0x55, 0x55), - EgaColors::BrightBlue => Color::RGB(0x55, 0x55, 0xff), - EgaColors::BrightGreen => Color::RGB(0x55, 0xff, 0x55), - EgaColors::BrightCyan => Color::RGB(0x55, 0xff, 0xff), - EgaColors::BrightRed => Color::RGB(0xff, 0x55, 0x55), - EgaColors::BrightMagenta => Color::RGB(0xff, 0x55, 0xff), - EgaColors::BrightYellow => Color::RGB(0xff, 0xff, 0x55), - EgaColors::White => Color::RGB(0xff, 0xff, 0xff), - }; - col - } - - pub fn from_u8(num: u8) -> Color { - let col = match num { - 0 => Color::RGB(0x00, 0x00, 0x00), - 1 => Color::RGB(0x00, 0x00, 0xaa), - 2 => Color::RGB(0x00, 0xaa, 0x00), - 3 => Color::RGB(0x00, 0xaa, 0xaa), - 4 => Color::RGB(0xaa, 0x00, 0x00), - 5 => Color::RGB(0xaa, 0x00, 0xaa), - 6 => Color::RGB(0xaa, 0x55, 0x00), - 7 => Color::RGB(0xaa, 0xaa, 0xaa), - 8 => Color::RGB(0x55, 0x55, 0x55), - 9 => Color::RGB(0x55, 0x55, 0xff), - 10 => Color::RGB(0x55, 0xff, 0x55), - 11 => Color::RGB(0x55, 0xff, 0xff), - 12 => Color::RGB(0xff, 0x55, 0x55), - 13 => Color::RGB(0xff, 0x55, 0xff), - 14 => Color::RGB(0xff, 0xff, 0x55), - 15 => Color::RGB(0xff, 0xff, 0xff), - _ => panic!("Unknown value for EgaColors: {}", num), - }; - col - } -} - #[derive(Clone, Copy)] pub struct ScreenBlock { pub blink: bool, @@ -162,49 +76,49 @@ impl ScreenBlock { let (blink, background, foreground, glyph) = ScreenBlock::index_to_attrs(num); ScreenBlock { blink, background, foreground, glyph } } -} -pub fn block_to_canvas( - canvas: &mut WindowCanvas, - textures: &Vec, - x: u8, - y: u8, - block: &ScreenBlock, - blink: &bool -) { - - let tex_idx = match blink { - false => block.as_texture_index(), - true => match block.blink { - false => block.as_texture_index(), - true => ScreenBlock::new(block.blink, block.background, block.background, block.glyph).as_texture_index(), - }, - }; - - // These next two assignments look odd mainly because of Rust's current - // issue with exclusive range patterns. See here: - // https://github.com/rust-lang/rust/issues/37854 - - let dest_x = match x { - n if (0..=GLYPH_COLUMNS - 1).contains(&n) => GLYPH_WIDTH as i32 * x as i32, - //0..=79 => GLYPH_WIDTH as i32 * x as i32, - _ => panic!("x is out of range! Must be 0..79 inclusive."), - }; - - let dest_y = match y { - n if (0..=GLYPH_ROWS - 1).contains(&n) => GLYPH_HEIGHT as i32 * y as i32, - //0..=24 => GLYPH_HEIGHT as i32 * y as i32, - _ => panic!("y is out of range! Must be 0..24 inclusive."), - }; - - match canvas.copy( - &textures[tex_idx], - Rect::new(0, 0, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)), - Rect::new(dest_x, dest_y, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)), + pub fn to_canvas( + &self, + canvas: &mut WindowCanvas, + textures: &Vec, + x: u8, + y: u8, + blink: &bool ) { - Ok(()) => (), - Err(err) => panic!("Unable to copy texture to canvas! SDL Error: {}", err), - }; + + let tex_idx = match blink { + false => self.as_texture_index(), + true => match self.blink { + false => self.as_texture_index(), + true => ScreenBlock::new(self.blink, self.background, self.background, self.glyph).as_texture_index(), + }, + }; + + // These next two assignments look odd mainly because of Rust's current + // issue with exclusive range patterns. See here: + // https://github.com/rust-lang/rust/issues/37854 + + let dest_x = match x { + n if (0..=GLYPH_COLUMNS - 1).contains(&n) => GLYPH_WIDTH as i32 * x as i32, + //0..=79 => GLYPH_WIDTH as i32 * x as i32, + _ => panic!("x is out of range! Must be 0..79 inclusive."), + }; + + let dest_y = match y { + n if (0..=GLYPH_ROWS - 1).contains(&n) => GLYPH_HEIGHT as i32 * y as i32, + //0..=24 => GLYPH_HEIGHT as i32 * y as i32, + _ => panic!("y is out of range! Must be 0..24 inclusive."), + }; + + match canvas.copy( + &textures[tex_idx], + Rect::new(0, 0, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)), + Rect::new(dest_x, dest_y, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)), + ) { + Ok(()) => (), + Err(err) => panic!("Unable to copy texture to canvas! SDL Error: {}", err), + }; + } } pub fn update(elapsed: u128) { @@ -212,13 +126,13 @@ pub fn update(elapsed: u128) { } pub fn render(canvas: &mut WindowCanvas, textures: &Vec, screen: &[ScreenBlock], blink: &bool) { - canvas.set_draw_color(EgaColors::from_enum(EgaColors::Black)); + canvas.set_draw_color::(EgaColors::Black.into()); canvas.clear(); for (idx, block) in screen.iter().enumerate() { let x = (idx % GLYPH_COLUMNS as usize) as u8; let y = (idx / GLYPH_COLUMNS as usize) as u8; - block_to_canvas(canvas, &textures, x, y, block, blink); + block.to_canvas(canvas, &textures, x, y, blink); } canvas.present(); } @@ -270,13 +184,13 @@ pub fn main() { for (x, pixel) in bits.iter().enumerate() { let offset = y * pitch + x * 3; if pixel == false { - buffer[offset] = EgaColors::from_u8(back).r; - buffer[offset + 1] = EgaColors::from_u8(back).g; - buffer[offset + 2] = EgaColors::from_u8(back).b; + buffer[offset] = EgaColors::u8_to_rgb(back).r; + buffer[offset + 1] = EgaColors::u8_to_rgb(back).g; + buffer[offset + 2] = EgaColors::u8_to_rgb(back).b; } else { - buffer[offset] = EgaColors::from_u8(fore).r; - buffer[offset + 1] = EgaColors::from_u8(fore).g; - buffer[offset + 2] = EgaColors::from_u8(fore).b; + buffer[offset] = EgaColors::u8_to_rgb(fore).r; + buffer[offset + 1] = EgaColors::u8_to_rgb(fore).g; + buffer[offset + 2] = EgaColors::u8_to_rgb(fore).b; } } } @@ -287,22 +201,22 @@ pub fn main() { glyph_textures.push(texture); } - let mut dos_screen: [ScreenBlock; TOTAL_SCREEN_BLOCKS] = [ScreenBlock::new(false, EgaColors::as_u8(EgaColors::Black), EgaColors::as_u8(EgaColors::Black), 0); TOTAL_SCREEN_BLOCKS]; + let mut dos_screen: [ScreenBlock; TOTAL_SCREEN_BLOCKS] = [ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::Black.into(), 0); TOTAL_SCREEN_BLOCKS]; for col in 0..60 { - dos_screen[col as usize] = ScreenBlock::new(false, EgaColors::as_u8(EgaColors::Black), EgaColors::as_u8(EgaColors::BrightYellow), 178); + dos_screen[col as usize] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178); } for row in 1..GLYPH_ROWS-1 { let idx = row as usize * GLYPH_COLUMNS as usize; - dos_screen[idx] = ScreenBlock::new(false, EgaColors::as_u8(EgaColors::Black), EgaColors::as_u8(EgaColors::BrightYellow), 178); - dos_screen[idx + 59] = ScreenBlock::new(false, EgaColors::as_u8(EgaColors::Black), EgaColors::as_u8(EgaColors::BrightYellow), 178); + dos_screen[idx] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178); + dos_screen[idx + 59] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178); } for col in 0..60 { let idx = (GLYPH_ROWS - 1) as usize * GLYPH_COLUMNS as usize + col as usize; - dos_screen[idx] = ScreenBlock::new(false, EgaColors::as_u8(EgaColors::Black), EgaColors::as_u8(EgaColors::BrightYellow), 178); + dos_screen[idx] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178); } - dos_screen[1030] = ScreenBlock::new(true, EgaColors::as_u8(EgaColors::Blue), EgaColors::as_u8(EgaColors::LightGray), 178); + dos_screen[1030] = ScreenBlock::new(true, EgaColors::Blue.into(), EgaColors::LightGray.into(), 178); let mut blink_tick: u128 = 0; let mut blink_state: bool = false;